Enfoque Estadístico del Aprendizaje / TP2

Planteo del Problema

El objetivo general de este trabajo es generar una serie de modelos de regresión que permitan explicar y predecir la asistencia al teatro, al cine y/o a espectáculos de música en vivo a partir de determinada información sociodemográfica.

Para ello trabajaremos con los datos que provienen de la edición 2017 de la Encuesta Nacional de Consumos Culturales, elaborada por el Sistema de Información Cultural de Argentina (SInCA) que pertenece al Ministerio de Cultura de la Nación. La ENCC se realiza cada cuatro años y busca conocer en profundidad el comportamiento de la población argentina respecto de los hábitos y consumos culturales. Indaga acerca de las frecuencias de consumo, el equipamiento cultural, el tiempo promedio de consumo o práctica, el grado de digitalización de los consumos culturales, las formas de realización de los consumos, los soportes utilizados, entre otros aspectos.

Según el informe Mujeres en la Cultura publicado por el SInCA, 8 de cada 10 personas que respondieron que no asisten a recitales por “motivos familiares, como tener hijos pequeños”, son mujeres. Al mismo tiempo, de cada 10 mujeres que fueron a recitales en 2017, 3 son sostén de hogar (entre los hombres la relación es más pareja: 5 de cada 10).

Partimos de la hipótesis de que las mujeres con hijxs y/o que conviven con personas mayores de 65 años van al cine, al teatro y a recitales en menor medida que los varones en la misma condición. En este sentido, buscamos predecir si una persona asistió en el último año o no a alguna de las tres actividades mencionadas, a partir de sus características sociodemográficas. Utilizaremos modelos de regresión logística ya que son los más eficientes para problemas de predicción de clases.

message = FALSE
# Cargamos librerías

library(readr)
library(tidyverse)
library(tidymodels)
library(modelr)
library(GGally)
library(pROC)
library(cowplot)
library(OneR)
library(rlang)
library(caret)
library(janitor)
library(corrr)
library(knitr)
library(kableExtra)
library(gridExtra)

Exploración de los datos

En este primer apartado confeccionamos el dataset con el que vamos a trabajar, a partir del dataset original de la Encuesta Nacional de Consumos Culturales. También llevamos a cabo una exploración inicial de las distribuciones de los datos, a partir de la apertura por la variable target.

Confección del dataset de trabajo y análisis de su estructura

El dataset completo de la Encuesta Nacional de Consumos Culturales tiene 2802 observaciones (respuestas) y 450 columnas (que refieren a las 117 preguntas del cuestionario). Tomando como guía la hipótesis de trabajo, armamos una versión resumida del dataset en la que conservaremos solo las variables que resultan pertinentes al problema.

#cargamos el dataset original
consumos <- read.csv("C:/Users/magal/OneDrive/Escritorio/EEA/tp2/encc17/encc_2017.csv", encoding = 'UTF-8')

#armamos la versión resumida con la que vamos a trabajar
consumos <- data.frame(consumos[,c(1,2,4:6,402,403,406,82,83,85,86,205,206,208,209,227,228)])

#visualizamos su estructura
glimpse(consumos)
Rows: 2,802
Columns: 18
$ id          <chr> "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16",~
$ pondera_dem <chr> "8608", "2869", "8765", "12838", "20223", "5474", "9999", "4072", "2977", "35896", "35~
$ region      <chr> "CENTRO", "CENTRO", "CENTRO", "CENTRO", "CENTRO", "CENTRO", "CENTRO", "CENTRO", "CENTR~
$ sexo        <chr> "Varón", "Varón", "Varón", "Varón", "Varón", "Mujer", "Mujer", "Mujer", "Mujer", "Muje~
$ edad        <chr> "19", "24", "30", "53", "70", "15", "25", "40", "53", "66", "18", "28", "57", "65", "1~
$ p104_1      <int> 3, NA, 0, 0, 0, 1, 1, 2, NA, 0, 1, 0, 0, 1, 2, 0, 1, 0, NA, NA, 1, 1, 0, 1, 0, 2, 2, 1~
$ p104_2      <int> 0, NA, 0, 0, 1, 0, 0, 0, NA, 2, 0, 0, 0, 1, 0, 0, 0, 0, NA, NA, 0, 1, 2, 0, 0, 0, 0, 0~
$ p107        <chr> "OTRO MIEMBRO DEL HOGAR", "LA PERSONA QUE FUE ENTREVISTADA", "LA PERSONA QUE FUE ENTRE~
$ p23         <chr> "NO", "SI", "NO", "SI", "NO", "NO", "SI", "SI", "NO", "NO", "SI", "SI", "NO", "SI", "N~
$ p23_1       <chr> "PORQUE LAS ENTRADAS ESTAN CARAS", "", "PORQUE NO LE INTERESA", "", "PORQUE NO LE INTE~
$ p23_2       <chr> "SI", NA, "NO", NA, "NO", "NO", NA, NA, "SI", "NO", NA, NA, "SI", NA, "NO", "NO", "SI"~
$ p24         <chr> NA, "TODAS O CASI TODAS LAS SEMANAS", NA, "ALGUNA VEZ AL AÑO O MENOS", NA, NA, "ALGUNA~
$ p63         <chr> "SI", "SI", "NO", "SI", "NO", "SI", "SI", "SI", "SI", "NO", "NO", "SI", "NO", "NO", "S~
$ p63_1       <chr> "", "", "PORQUE IR AL CINE ES MUY CARO", "", "PORQUE NO LE INTERESA", "", "", "", "", ~
$ p63_2       <chr> NA, NA, "NO", NA, "NO", NA, NA, NA, NA, "NO", "NO", NA, "SI", "NO", NA, NA, NA, NA, NA~
$ p64         <chr> "ALGUNA VEZ AL AÑO O MENOS", "ALGUNA VEZ CADA 3 MESES", NA, "ALGUNA VEZ AL AÑO O MENOS~
$ p68         <chr> "NO", "SI", "NO", "NO", "SI", "NO", "NO", "NO", "NO", "NO", "NO", "NO", "NO", "NO", "N~
$ p69         <chr> NA, "ALGUNA VEZ CADA 3 MESES", NA, NA, "ALGUNA VEZ CADA 3 MESES", NA, NA, NA, NA, NA, ~
#eliminamos fila con error de carga
consumos = consumos[-2740,]
#eliminamos fila con error de carga
consumos = consumos[-1950,]

Las variables del set incluyen id, sexo de la persona encuestada, edad, región de residencia, así como también:

  • p104_1: ¿Cuántas personas (con la que convive) son menores de 15 años?
  • p104_2: ¿Cuántas personas (con la que convive) son mayores de 65 años?
  • p107: ¿Podría decirme cuál es la persona que más aporta a los gastos del hogar, es decir, el principal sostén económico del hogar (PSH)?
  • p23: ¿Concurrió a recitales o presentaciones de música en vivo durante el último año?
  • p23_1 [solo para quienes responden NO en p23]: ¿Cuál es el motivo principal por el que no concurre a recitales?
  • p23_2 [solo para quienes responden NO en p23]: Anteriormente, ¿Aconstumbraba a ir a recitales?
  • p24 [solo para quienes responden SI en p23]: ¿Con qué frecuencia concurrió a recitales el último año?
  • p63: ¿Concurrió al cine el último año?
  • p63_1 [solo para quienes responden NO en p63]: ¿Cuál es el motivo principal por el que no concurre al cine?
  • p63_2 [solo para quienes responden NO en p63]: Anteriormente, ¿Aconstumbraba a ir al cine?
  • p64 [solo para quienes responden SI en p63]: ¿Con qué frecuencia concurrió al cine el último año?
  • p68: ¿Concurrió a espectáculos teatrales el último año?
  • p69 [solo para quienes responden SI en p68]: ¿Con qué frecuencia concurrió a espectáculos teatrales el último año?

Se observa que las variables seleccionadas pueden agruparse en dos bloques: aquellas relacionadas con las prácticas culturales de las personas entrevistadas y las relacionadas con su información sociodemográfica.

La pregunta 104 fue incluída porque nos interesa analizar, entre otras cosas, si el hecho de convivir con niñxs, adolecentes y/o adultos mayores está relacionado con la frecuencia de asistencia a prácticas culturales de la persona entrevistada, partiendo de la hipótesis de que tener a cargo tareas de cuidado puede interferir en la concurrencia a este tipo de actividades. En este sentido, nos interesa analizar si la relación entre ambas variables es similar o no entre varones y mujeres.

Creación de la variable dicotómica “convive_niñxs_adultosmayores”. Las variables originales de la pregunta 104 registran la cantidad de menores de 15 años o mayores de 65 años que conviven con la persona entrevistada. Se dicotomizaron ambas variarbles y se creó una nueva que toma valor 1 si la persona entrevistada convive con al menos un menor de 15 años y/o al menos un mayor de 65.

#se reemplazan los valores NA por 0 (refieren a las personas que viven solas y no respondieron estas preguntas)
consumos <- mutate_at(consumos, c("p104_1", "p104_2"), ~replace(., is.na(.), 0))

#Se dicotomizan las varibales p104_1 / p104_2
consumos$p104_1_RECAT <- ifelse(consumos$p104_1 != 0, 1, 0)
consumos$p104_2_RECAT <- ifelse(consumos$p104_2 != 0, 1, 0)

#se crea una nueva columna que se llama “convive_niñxs_adultosmayores” y toma valores 1 si la persona encuestada vive con menores de 15 años y/o mayores de 65
consumos <- consumos %>% 
  mutate(convive_niñxs_adultosmayores = case_when(p104_1_RECAT == 1 | p104_2_RECAT == 1 ~ 1))

#se reemplazan los valores NA por 0
consumos <- mutate_at(consumos, c("convive_niñxs_adultosmayores"), ~replace(., is.na(.), 0))
## La transformamos en una variable categórica
consumos = consumos %>% 
  mutate(convive_niñxs_adultosmayores = case_when(convive_niñxs_adultosmayores== 1 ~ "SI",
                                        convive_niñxs_adultosmayores== 0 ~ "NO"))

Creación de la variable categórica “franja_etaria”. La variable edad en el dataset original es una variable continua. Aplicamos el mismo criterio de construcción de franjas etarias que el Sistema de Información Cultural de Argentina (SInCA) y la discretizamos.

#transformamos la variable edad en numérica
consumos$edad <- as.numeric(consumos$edad)

#creamos la variable franja_etaria
consumos$franja_etaria = cut(consumos$edad,
                              breaks = c(12,17,29,49,64,92),
                              labels = c("de12a17","de18a29", "de30a49", "de50a64", "65omás"))
                                   

Definimos nuestra variable target: asistencia al cine, al teatro y/o a un espectáculo musical en el último año

Lo que nos interesa predecir es si la persona entrevistada fue al cine, al teatro y/o a un recital en vivo el último año, a partir de sus características sociodemográficas. Agrupamos estas tres variables en una nueva dicotómica “asistencia”, que expresa 1= “Sí, asistió al menos a uno de estos eventos en el último año” o 0= “No asistió a ninguno”. A continuación, analizamos sus distribuciones por separado y la relación con el resto de las variables.

consumos %>% tabyl(p23)%>% #asistió a recitales
  arrange(desc(percent), n)%>%
  fashion()
consumos %>% tabyl(p63)%>% #asistió a cines
  arrange(desc(percent), n)%>%
  fashion()
consumos %>% tabyl(p68)%>% #asistió a teatros
  arrange(desc(percent), n)%>%
  fashion()

Vemos que hay mayor desbalance de clases en la asistencia teatros, seguido por asistencia a recitales y, finalmente, a los cines.

#Generamos la nueva variable
consumos <- consumos %>% 
  mutate(asistencia = case_when(p23=="SI" |  p68=="SI" |  p63=="SI" ~ 1,
                                 TRUE ~ 0))
#observamos su distribución
consumos %>% tabyl(asistencia)%>% 
  arrange(desc(percent), n)%>%
  fashion()

El 52% de las personas entrevistadas asistió al cine, al teatro y/o a un espectáculo de música en vivo el último año. Mientras que el 47% no lo hizo.


# graficamos con ggpairs coloreando por variable a predecir
g <- consumos %>% 
        select("asistencia","sexo","region", "franja_etaria", "p107", "convive_niñxs_adultosmayores") %>% 
        ggpairs(title = "Apertura por variable target: asistencia al cine, al teatro y/o a recitales en el último año",
                mapping = aes(colour= factor(asistencia)),
                progress = FALSE, 
                lower=list(combo=wrap("facethist", binwidth=0.8)), legend = 25) +
        theme(axis.text.x = element_text(angle = 90, hjust = 1)) + 
        theme_bw() +
        scale_fill_brewer(palette="Set1") +
        scale_color_brewer(palette="Set1")
g

Al realizar la apertura por la variable target, en el análisis gráfico se observa que la clase asistencia es facilmente discriminable, ambas categorías se separan con bastante claridad. Recordemos que las clases estan bastante balanceadas.

Vemos que para la variable sexo las distribuciones de asistencia son proporcionales tanto para mujeres como para varones, siendo del total de mujeres encuestadas la mitad, aproximadamente, las que asisten a eventos de este tipo; al igual que para el caso de los varones: apenas un poco más de la mitad de los encuestados asistieron en el último año al cine, al teatro y/o a recitales.

Para el caso de la variable que creamos convive_niñxs_adultosmayores ocurre algo similar que para la variable sexo: de los que sí conviven con niñxs, adolecentes y/o adultos mayores (que son la mayoría de las personas encuestadas) aproximadamente la mitad sí asistió a eventos en el último año y la otra mitad no asistió, mientras que dentro de los que no conviven con niñxs o adultos mayores, es un poco más la cantidad de los que sí asisten. Veamos ahora el caso de las personas que convien con niñxs, adolecentes y/o adultos mayores que asistieron a espectáculos durante el último año vs. los que no lo hicieron, según sexo: nuevamente se observa que hay una distribución bastante pareja entre mujeres y varones.

Por otro lado, si observamos con mayor detalle, las variables franja_etaria y región de residencia son las que presentan mayores variaciones. También hay mayor variabilidad en las variable p107, principal sostén económico (PSE). Lo cual resulta interesante para analizar adicionalmente en los modelos que siguen.

Finalmente, visualizamos las primeras seis filas de nuestro dataset de trabajo definitivo.

head(consumos)

Modelo de Regresión Logística

En este apartado creamos una serie de modelos simples y múltiples de regresión logística e interpretamos sus coeficientes.

Queremos estimar P(Asistió=1|X)=P(X) para cada individuo y a partir de ello poder definir un punto de corte para predecir quiénes asistieron al cine, al teatro y/o a espectáculos de música en vivo el último años y quienes no.

Partición del dataset en Test y Train

Para evaluar los modelos vamos a realizar una partición entre dataset de entrenamiento (75%) y testeo (25%) usando la función initial_split del paquete rsample de tidymodels. El dataset de entrenamiento quedará con un total de 2100 casos y el de testeo, con 700.

# Fijamos semilla
set.seed(2021)

# Partición Train y Test, indicando proporción
train_test <- initial_split(consumos, prop = 0.75)
train_data <- training(train_test)
test_data <- testing(train_test)

# Vemos las dimensiones de cada particion
train_data %>%
  dim_desc() 
[1] "[2,100 x 23]"
test_data %>%
  dim_desc()
[1] "[700 x 23]"

Resulta pertinente analizar las distribuciones de la variable target en ambos datasets.

# calculamos la distribución de clase en cada dataset
train <- train_data %>% 
  group_by(asistencia) %>% 
  summarise(numero_casos=n()) %>%
  mutate(prop = round(prop.table(numero_casos)*100,2))
test <- test_data %>% 
  group_by(asistencia) %>% 
  summarise(numero_casos=n()) %>%
  mutate(prop = round(prop.table(numero_casos)*100,2))

# armamos una tabla conjunta para graficar
distrib = cbind(rbind(train, test), dataset = c("train", "train", "test", "test"))
distrib

# graficamos las distribuciones
ggplot(distrib, aes(x = asistencia, y = prop, fill = factor(asistencia), label = prop)) + 
  geom_bar(stat="identity", position = "dodge") + facet_wrap(~ dataset) +
  theme(axis.text.x = element_text(angle = 90, hjust = 1)) +
  labs(x = "Asistencia al cine, teatro y/o recitales en el último año", y = "Proporción en %", title = "Proporción de asistencia por dataset") + 
  theme_bw() +
  scale_fill_brewer(palette="Set1")

Observamos que prácticamente no existe desbalance en las clases de la variable a predecir. Esto es importante porque clases desbalanceadas pueden afectar las estimaciones del modelo y su clasificación final.

Creación de modelos

La Regresión Logística es una de las aplicaciones posibles de los Modelos Lineales Generalizados (GLM). La funcíón glm(), al igual que la función lm(), toma como argumentos una formula y los datos, pero este caso también se debe especificar el argumento family: indicamos la distribución del error y la función link que vamos a utilizar en el modelo.

Algunas familias son:

  • Binomial: link=logit

  • Poisson: link=log

  • Gaussiana: link=identidad

Como estamos trabajando con un fenómeno que suponemos tiene una distribución binomial, así lo especificamos en el parámetro family. A continuación, realizamos una serie de modelos de regresión logística para predecir la asistencia o no al cine, teatro y/o recitables en el último año de las personas entrevistadas, en función las características sociodemográficas seleccionadas.

Para crear varios modelos de regresión logística utilizamos la función formulas del paquete modelr, creamos así un objeto que contiene todas las fórmulas que vamos a utilizar. En .response especificamos la variable respuesta de nuestras fórmulas y luego nombramos las fórmulas que queramos armar.

Luego de crear las formúlas, creamos los modelos y los analizamos de forma comparativa.

# Creación de fórmulas
logit_formulas <- formulas(.response = ~ asistencia,
                           convive = ~ convive_niñxs_adultosmayores, 
                           region = ~ region,
                           sexo = ~ sexo, 
                           franja_etaria = ~ franja_etaria, 
                           PSE = ~ p107,
                           sexo_PSE = ~ sexo + p107, 
                           sexo_convive = ~ sexo + convive_niñxs_adultosmayores, 
                           convive_region_PSE = ~ convive_niñxs_adultosmayores + region + p107,
                           franja_region_PSE = ~ franja_etaria + region + p107,
                           )

logit_formulas # observamos el objeto formulas
$convive
asistencia ~ convive_niñxs_adultosmayores

$region
asistencia ~ region

$sexo
asistencia ~ sexo

$franja_etaria
asistencia ~ franja_etaria

$PSE
asistencia ~ p107

$sexo_PSE
asistencia ~ sexo + p107
<environment: 0x00000277444e7b78>

$sexo_convive
asistencia ~ sexo + convive_niñxs_adultosmayores
<environment: 0x000002773fdee1d8>

$convive_region_PSE
asistencia ~ convive_niñxs_adultosmayores + region + p107
<environment: 0x0000027747246bd0>

$franja_region_PSE
asistencia ~ franja_etaria + region + p107
<environment: 0x0000027742f5ce80>

Procedemos a crear los modelos a partir de estas fórmulas.

models <- data_frame(logit_formulas) %>% # dataframe a partir del objeto formulas
  mutate(models = names(logit_formulas), # columna con los nombres de las formulas
         expression = paste(logit_formulas), # columna con las expresiones de las formulas
         mod = map(logit_formulas, ~glm(., family = 'binomial', data = train_data))) 
models

Interpretación: Modelos simples

Analizamos los primeros cinco modelos, aquellos que tienen un único predictor. Usamos la función tidy para obtener los parámetros estimados para estos cinco modelos.

models %>% 
  filter(models %in% c('region','sexo','franja_etaria','convive','PSE')) %>%
  mutate(tidy = map(mod, tidy)) %>%
  unnest(tidy) %>% 
  mutate(estimate=round(estimate,5), # redondeamos valores para facilitar lectura
         p.value=round(p.value,4))
NA

Interpretación de los coeficientes:

Modelo convive

β0 = 0.23136 corresponde a las personas que no conviven con niñxs, adolecentes y/o aldultos mayores.

β1 = -0.18525 representa a las personas que conviven con niñxs, adolecentes y/o adultos mayores y el signo negativo del coeficiente indica que la probabilidad de asistencia al cine, teatro y/o recitales en el último año se reduce en comparación con los que no conviven. El p-valor>0.05 indica que se trata de una diferencia estadísticamente significativa.

Modelo región

β0 = 0.93982 corresponde a las personas entrevistadas que viven en CABA.

β1 a β6 = Los coeficientes estimados en todos casos son negativos, indicando que la probabilidad de haber asistido al cine, teatro y/o recitales en el último año de las personas que viven en las distintas regiones del país disminuye en comparación con las personas que viven en CABA. Obervando los valores de los coeficientes, las categorías se pueden ordenar de menor a mayor. En este sentido, es esperable que la probabilidad de haber asistido al cine, teatro y/o recitales en el último año sea aún menor para el NOA (-1.43) que para la quienes viven en la región CENTRO (-0.38).

En todos los casos, los p-valores son menores que 0.05, lo cual indica que las diferencias con CABA (categoría basal) son estadísticamente significativas.

Modelo sexo

β0 = 0.13887 corresponde a las mujeres entrevistadas.

β1 = -0.06052 representa a los varones entrevistados e indica que la probabilidad de asistencia al cine, teatro y/o recitales se reduce en comparación a las mujeres. Sin embargo, el p-valor>0.05 indica que no se trata de una diferencia estadísticamente significativa.

Modelo franja_etaria

β0 = 0.83704 corresponde a las personas entrevistadas que tienen entre 12 y 17 años.

β1 a β4 = Los coeficientes estimados en todos casos son negativos, indicando que la probabilidad de haber asistido al cine, al teatro y/o a recitales en el último año de las personas de 18 años o más disminuye en comparación con los más jóvenes. Obervando los valores de los coeficientes, las categorías se puede ordenar de menor a mayor. En este sentido, es esperable que la probabilidad de haber asistido al cine en el último año sea aún menor para las personas de 65 años o más (-1.90) que para la quienes tienen entre 18 y 29 años (-0.18). Al mirar los p-valores, se observa que sólo para la franja etaria de 18 a 29, la diferencia con la categoría basal (personas de 12 a 17) no es estadíticamente significativa (p-valor>0.05).

Modelo PSE (Principal Sostén Económico del Hogar)

β0 = -0.15415 corresponde a las personas entrevistadas que son el principal sostén económico de hogar (PSE).

β1 = 0.55218 representa a quienes no son el PSE. El signo positivo del coeficiente indica que la probabilidad de asistencia al cine, al teatro y/o a recitales en el último año es mayor para quienes no son jefxs de hogares que para quienes sí lo son. El p-valor<0.05 indica que se trata de una diferencia estadísticamente siginficativa.

Interpretación: Modelos múltiples

A continuación analizamos los cuatro modelos múltiples.

models %>% 
  filter(models %in% c('sexo_PSE','sexo_convive','convive_region_PSE', 'franja_region_PSE')) %>%
  mutate(tidy = map(mod, tidy)) %>%
  unnest(tidy) %>% 
  mutate(estimate=round(estimate,5), # redondeamos valores para facilitar lectura
         p.value=round(p.value,4))
NA

Interpretación de los coeficientes:

Modelo sexo_PSE

β0 = -0.21203. Al tratarse de un modelo con dos variables explicativas categóricas, el Intercept representa en este caso a las mujeres que son el principal sostén económico de sus hogares.

β1 = 0.09241. Corresponde a la categoría varón de la variable sexo. Se observa que el coeficiente es positivo. Esto significaría que la probabilidad de asistencia al cine, al teatro y/o a recitales es mayor respecto a la de las mujeres, dadas las demás variables del modelo. Sin embargo, el p-valor>0.05 indica que no se trata de una diferencia estadísticamente significativa.

β2 = 0.57689. Corresponde a la categoría otro miembro del hogar de la variable PSE. Se observa que el coeficiente es positivo. Esto significa que la probabilidad de asistencia es mayor respecto a la de las personas que son el principal sostén económico del hogar donde viven, dadas las demás variables del modelo. El p-valor<0.05 indica que se trata de una diferencia estadísticamente significativa.

Modelo sexo_convive

β0 = 0.26772. En este caso, el Intercept representa a las mujeres que no conviven con niñxs, adolecentes y/o adultos mayores.

β1 = -0.06854 y β2 = -0.18851. Ambos coeficientes son negativos, por lo que representarían una probablilidad menor de asistencia al cine en el último año para ambas categorías, dadas las demás variables del modelo. Sin embargo, al mirar los p-valores, se observa que sólo el hecho de convivir o no con niñxs, adolecentes y/o adultos mayores representa una diferencia estadísticamente significativa (p-valor=0.0420).

Modelo convive_region_PSE

β0 = 0.79411. En este caso, el Intercept representa a las personas que no conviven con niñxs, adolecentes ni adultos mayores, que viven en CABA y son el principal sostén económico del hogar donde viven.

β1 = -0.22181. Corresponde a las personas que conviven con niñxs, adolecentes y/o adultos mayores. El signo negativo del coeficiente indica que la probablidad de haber asistido al cine, al teatro y/o a recitales en el último año disminuye, en comparación con las personas que no conviven con niñxs, adolecentes ni adultos mayores. El p-valor < 0.05, indica que se trata de una diferencia estadísticamente significativa.

β2 a β7. Corresponden a las personas que no viven en CABA. En línea con lo que se obervó en el modelo simple, todos los coeficientes son negativos, por lo que en las seis regiones del país analizadas la probablidad de haber asistido al cine, al teatro y/o a recitales en el último año disminuye en relación a quienes viven en CABA. Los p-valores<0.05 indican que las diferencias son estadícticamente significativas en todos los casos.

β8= 0.66663. Correspode a las personas que no son el principal sostén económico del hogar. El coeficiente positivo significa que las personas que no son el PSE, tienen una probablidad mayor de haber asistido al cine, al teatro y/o a recitales el último año, en comparación con las que sí lo son, dadas el resto de las variables del modelo. El p-valor<0.05 indica que se tarta de una diferencia estadícticamente significativa.

Modelo franja_region_PSE

β0 = 1.86907. En este caso, en el Intercept representa a las personas entre 12 y 17 años, que viven en CABA y son el principal sostén económico del hogar donde viven.

β1 a β4. Corresponde a las personas de 18 años o más, divididas por franjas etarias. En línea con lo que se obervó en el modelo simple, todos los coeficientes son negativos, por lo que para las personas mayores de 18 años la probablidad de haber asistido disminuye en relación a quienes son menores de edad. Los p-valores<0.05 (en todos los casos, menos para las personas que tienen entre 18 y 29 años) indican que las diferencias son estadícticamente significativas.

β5 a β10. Corresponden a las personas que no viven en CABA. Como en el modelo anterior, todos los coeficientes son negativos, por lo que en las seis regiones del país analizadas la probablidad de haber asistido al cine, al teatro y/o a recitales en el último año disminuye en relación a quienes viven en CABA, dadas el resto de las variables del modelo. Los p-valores<0.05 indican que las diferencias son estadícticamente significativas en todos los casos.

β11= 0.04920. Correponde a las personas que no son el principal sostén económico del hogar en el que viven. El coeficiente de signo positivo indicaría que las personas que no están a cargo económicamente de su hogar tienen mayor probabilidad de haber ido al cine, al teatro y/o a recitales en el último año que quienes sí lo están, dadas el resto de las variables del modelo. Sin emabrgo, el p-valor>0.05 indica que no se trata de una diferencia estadísticamente significativa.

Evaluación de todos los modelos

El paso siguiente es evaluar los modelos de forma comparativa para analizar cuál de ellos son los que minimizan la deviance (como expresión de los residuos).

Con map() agregamos la función glance para traer información relevante para la evaluación del modelo. Con unnest() accedemos a dicha información. Por último, agregamos una columna con el porcentaje de deviance explicado por cada modelo y ordenamos el dataset según su valor de deviance.

# Calcular las medidas de evaluación para cada modelo
models <- models %>% 
  mutate(glance = map(mod,glance))

# Obtener las medidas de evaluacion de interes
models %>% 
  unnest(glance) %>%
  
  # Calculamos la deviance explicada
  mutate(perc_explained_dev = 1-deviance/null.deviance) %>% 
  select(-c(models, df.null, AIC, BIC)) %>% 
  arrange(deviance)

Se observa que el modelo que más reduce la deviance del modelo (como expresión de los residuos) es el que incluye como variables explicativas la franja etaria a la que pertenece la persona entrevistada, la región del país en la que vive y si es o no el principal sostén económico de su hogar (PSE). A partir del cáclulo de deviance explicada, observamos que dicho modelo explica aproximadamente el 12% de la variabilidad del fenómeno que buscamos captar.

Gráficos de Evaluación

A continuación, graficamos las métricas del mejor modelo hallado (asistencia~ franja_etaria + region + p107) vs. el de mayor deviance (asistencia~ sexo)

Comenzamos agregando las predicciones con augment con el parámetro type="response". La función augment hereda el argumento type.predict de la función predict.

  • Si type.predict = 'link' la predicción es en términos de la función link. En nuestro caso son el logaritmo de las odds, es decir, los valores que toma la expresión logit.

  • Si type.predict = 'response' la predicción son las probabilidades de que la observación pertenezca a la clase positiva. En nuestro caso, devuelve la probabilidad de la que persona haya asistido al cine, al teatro y/o a recitales durante el último año.

# Añadir las predicciones
models <- models %>% 
  mutate(pred= map(mod, augment, type.predict = "response"))

#Observaciones con probabilidad más baja (MEJOR MODELO)
models$pred$franja_region_PSE %>% arrange(.fitted) %>% head(10)
#Observaciones con probabilidad más alta (MEJOR MODELO)
models$pred$franja_region_PSE %>% arrange(desc(.fitted)) %>% head(10)

Guardamos las predicciones para los modelos mencionados.

# Modelo bueno
prediction_full <- models %>% 
  filter(models=="franja_region_PSE") %>% 
  unnest(pred)
#Modelo malo
prediction_bad <- models %>% 
  filter(models=="sexo") %>% 
  unnest(pred)
  1. Graficamos violin plots.
# graficamos el modelo completo
violin_full = ggplot(prediction_full, aes(x=asistencia, y=.fitted, group=asistencia, fill=factor(asistencia))) + 
  geom_violin() +
  theme_bw() +
  guides(scale="none") +
  labs(title='Violin plot', subtitle='Modelo bueno', y='Predicted probability')
# graficamos el modelo malo
violin_bad = ggplot(prediction_bad, aes(x=asistencia, y=.fitted, group=asistencia, fill=factor(asistencia))) + 
  geom_violin() + 
  theme_bw() +
  guides(scale="none") +
  labs(title='Violin plot', subtitle='Modelo malo', y='Predicted probability')
# mostramos ambos
plot_grid(violin_bad, violin_full)

En los gráficos de violin observamos: En el eje de abscisas la clase verdadera (asistió o no asistió en el último año al teatro, al cino y/o a un recital). En el eje de ordenadas la probabilidad predicha por nuestro modelo. El gráfico nos muestra la distribución de la cantidad de observaciones por su clase real y la probabilidad que le asigna nuestro modelo.

Lo que observamos en el modelo “franja_region_PSE” (modelo bueno) es que para el valor real 1 (asistió), la mayor parte de las observaciones se concentran en la mitad superior del gráfico, que corresponde a las probabilidad predichas más altas de pertenecer a la clase 1. Lo inverso, aunque con menor claridad, se observa para el valor 0 (no asistió).

  1. Realizamos a continuación el gráfico de Hosmer-Lemeshow.

Se genera una función para realizar un gráfico de Hosmer-Lemeshow para un dataset. Para ello se fijan los siguientes parámetros:

  • dataset: conjunto de datos

  • predicted_column: columna con la probabilidad predicha

  • class_column: columna con la clase a predecir

  • possitive_value: valor de la clase a predecir

  • bins: cantidad de grupos del gráfico

  • color: color de los puntos

  • nudge_x: desplazamiento de la etiqueta en el eje x

  • nudge_y: desplazamiento de la etiqueta en el eje y


  # Función para generar los gráficos
Hosmer_Lemeshow_plot <- function(dataset, predicted_column, class_column, bins, positive_value, color='forestgreen', nudge_x=0, nudge_y=0.05){
  
  # Asignar los grupos a las observaciones de acuerdo a la probabilidad predicha
  dataset['group'] <- bin(dataset[predicted_column], nbins = bins, method = 'l', labels=c(1:bins))
  
  # Contar la cantidad de casos positivos por grupo
  positive_class <- dataset %>% filter(!!sym(class_column)==positive_value) %>% group_by(group) %>% count()
  
  # Obtener la media de las predicciones por grupo
  HL_df <- dataset %>% group_by(group) %>% summarise(pred=mean(!!sym(predicted_column)), count=n()) %>%
            inner_join(.,positive_class) %>%
            mutate(freq=n/count)
  # Gráfico 
  HM_plot <- ggplot(HL_df, aes(x=pred, y=freq)) + 
    geom_point(aes(size=n), color=color) +
    geom_text(aes(label=n),nudge_y = nudge_y)+
    geom_abline(slope = 1, intercept = 0, linetype='dashed') + 
    theme_bw() +
    labs(title='Hosmer-Lemeshow', size='Casos', x="Probabilidad Predicha", y="Frecuencia observada")
  return(HM_plot)
}

Generamos los gráficos pasandole los parámetros.

# modelo bueno
Hosmer_Lemeshow_plot(prediction_full, '.fitted', 'asistencia', 10, 1, color = "forestgreen") +
  labs(subtitle="Modelo bueno")

# modelo malo
Hosmer_Lemeshow_plot(prediction_bad, '.fitted', 'asistencia', 10, 1, color = "firebrick") + labs(subtitle="Modelo malo")

En los gráficos de Hosmer-Lemeshow observamos:

  • En el eje de abscisas la probabilidad predicha de supervivencia.

  • En el eje de ordenadas la frecuencia de clase, el cociente entre cantidad de individuos Survived y el total de individuos.

  • La línea punteada designa la igualdad entre probabilidad predicha y frecuencia de clase.

  • Los círculos, que se construyen de la siguiente manera:Se dividen a las observaciones en bins en base a la probabilidad predicha. Se calcula la frecuencia de clase para cada bin. En base a estas dos coordenadas se ubica al círculo en el gráfico. El número y tamaño indican la cantidad de observaciones en dicho grupo. Aquellos círculos que se ubiquen por encima de la línea punteada indican que el modelo está subestimando la probabilidad para dichos grupos. Mientras que si los círculos se ubican por debajo el modelo está sobreestimando la probabilidad para dichos grupos.

El el modelo asistencia~ franja_etaria + region + p107 observamos que la mayoría de los circulos se ubican sobre la línea punteada. Es decir, se presenta mayor igualdad entre probabilidad predicha y frecuencia de asistencia. Sin embargo pareciera existir cierta sobreestimación de la probabilidad (alrededor de 0.68) para 84 observaciones de poco más de 0.60 de frecuencia observada. Lo contrario sucede con el grupo de 29 observaciones y de 129 observaciones de las probabilidades predichas de valor 0.35 y 0.95, respectivamente.

  1. Graficamos las Curvas ROC
# Calculamos curvas ROC
roc_full <- roc(response=prediction_full$asistencia, predictor=prediction_full$.fitted)
roc_bad <- roc(response=prediction_bad$asistencia, predictor=prediction_bad$.fitted)

Graficamos ambas en un mismo plot.

ggroc(list(full=roc_full, bad=roc_bad), size=1) + 
  geom_abline(slope = 1, intercept = 1, linetype='dashed') +
  theme_bw() + 
  labs(title='Curvas ROC', color='Modelo')

print(paste('AUC: Modelo bueno', round(roc_full$auc,3)))
[1] "AUC: Modelo bueno 0.724"
print(paste('AUC: Modelo malo', round(roc_bad$auc,3)))
[1] "AUC: Modelo malo 0.508"

Vemos que la curva ROC del “modelo malo” casi se pega a la línea punteada, indicando que los valores de predicciones de este modelo se asemejan a “tirar una moneda”, es decir, existe casi la misma probabilidad (0.50) de predecir que la persona haya ido al cine, al teatro y/o a recitales o no en el último año, cuando se hace la estimación en función de la variable sexo solamente. Por otra parte, vemos que nuestro mejor modelo se aleja bastante de esta situación (AUC 0.724).

Punto de corte

Hasta ahora hemos evaluado nuestro mejor modelo de manera general, pero el resultado final debe consistir en asignar a la persona una clase predicha. En nuestro caso, debemos establecer un punto de corte según el cual podamos separar a las personas que fueron el último año al teatro, al cine y/o a recitales de las que no.

A continuación, probamos varios puntos de corte y graficamos el accuracy, la sensibilidad o recall, la especificidad y la precisión para cada uno de ellos.

Clases predichas / Clases Negativa Positiva
Negativa True Neg False Neg
Positiva False Pos True Pos

Recordemos que:

\(accuracy = \frac{TP+TN}{TP+FP+FN+TN}\)

\(sensitivity = recall = \frac{TP}{TP+FN}\)

\(specificity = \frac{TN}{TN+FP}\)

\(precision = \frac{TP}{TP+FP}\)

prediction_metrics <- function(cutoff, predictions=prediction_full){
  tab <- predictions %>% 
    mutate(predicted_class = if_else(.fitted > cutoff, 1, 0),
           asistencia = factor(asistencia))
  u <- union(tab$predicted_class, tab$asistencia)
  t <- table(factor(tab$predicted_class, u), factor(tab$asistencia, u))
  confusionMatrix(t, positive = "1") %>%
    tidy() %>%
    select(term, estimate) %>%
    filter(term %in% c('accuracy', 'sensitivity', 'specificity', 'precision')) %>%
    mutate(cutoff = cutoff)
}
cutoffs = seq(0.05,0.95,0.01)
logit_pred = map_df(cutoffs, prediction_metrics) %>% 
  mutate(term = as.factor(term), estimate = round(estimate, 3))
ggplot(logit_pred, aes(cutoff,estimate, group=term, color=term)) + geom_line(size=1) +
  theme_bw() +
  labs(title= 'Accuracy, Sensitivity, Specificity y Precision', subtitle= 'Modelo bueno', color="")

En el gráfico observamos que hay una tendencia creciente en cuanto a la estimación de nuestro mejor modelo para puntos de corte mayores en cuanto a specificity, mientras que existe una tendencia decreciente para puntos de corte mayores al estimar la sensibility. En general, el accuracy y la precision toman valores por encima del 0.5 para cualquier punto de corte, pero el accuracy no toma nunca valores mayores al 0.7, mientras que la precision tiene tendencia creciente a mayor valor de punto de corte.

En nuestro caso, por la naturaleza del problema abordado, elegimos un punto de corte para ponderar la sensibility, o tasa de verdaderos positivos, en 0.4; ya que en ese punto tenemos un valor relativamente alto para esta tasa, resignando los valores de specificity pero con estimaciones por encima del 0.5 tanto de accuracy como precision.

Dataset de testing

Seleccionamos el modelo asistencia~ franja_etaria + region + p107, ya que es el que maximizaba el porcentaje de deviance explicada y, en base a lo que se observa en el gráfico anterior, definimos un punto de corte en 0.4.

Calculamos la matriz de confusión para los datasets de train y test.

sel_cutoff = 0.4 
# Creamos el modelo
full_model <- glm(logit_formulas$franja_region_PSE, family = 'binomial', data = train_data)
# calculamos las predicciones sobre el dataset de train
table_train = augment(x = full_model, type.predict='response')
# Clasificamos utilizamos el punto de corte
table_train = table_train %>% 
  mutate(predicted_class = if_else(.fitted>sel_cutoff, 1, 0) %>% as.factor(), 
         asistencia = factor(asistencia))
# Creamos la matriz de confusión
confusionMatrix(table(table_train$predicted_class, table_train$asistencia), positive = "1")
Confusion Matrix and Statistics

   
      0   1
  0 361 114
  1 632 993
                                          
               Accuracy : 0.6448          
                 95% CI : (0.6239, 0.6653)
    No Information Rate : 0.5271          
    P-Value [Acc > NIR] : < 2.2e-16       
                                          
                  Kappa : 0.2678          
                                          
 Mcnemar's Test P-Value : < 2.2e-16       
                                          
            Sensitivity : 0.8970          
            Specificity : 0.3635          
         Pos Pred Value : 0.6111          
         Neg Pred Value : 0.7600          
             Prevalence : 0.5271          
         Detection Rate : 0.4729          
   Detection Prevalence : 0.7738          
      Balanced Accuracy : 0.6303          
                                          
       'Positive' Class : 1               
                                          
# Agregamos la predicciones al dataset de testeo
table_test = augment(x = full_model, newdata=test_data, type.predict='response') 
# Clasificamos utilizamos el punto de corte
table_test = table_test %>% 
  mutate(predicted_class = if_else(.fitted>sel_cutoff, 1, 0) %>% as.factor(), 
         asistencia = factor(asistencia))
# Creamos la matriz de confusión
confusionMatrix(table(table_test$predicted_class, table_test$asistencia), positive = "1")
Confusion Matrix and Statistics

   
      0   1
  0 115  46
  1 222 317
                                        
               Accuracy : 0.6171        
                 95% CI : (0.58, 0.6533)
    No Information Rate : 0.5186        
    P-Value [Acc > NIR] : 9.427e-08     
                                        
                  Kappa : 0.2186        
                                        
 Mcnemar's Test P-Value : < 2.2e-16     
                                        
            Sensitivity : 0.8733        
            Specificity : 0.3412        
         Pos Pred Value : 0.5881        
         Neg Pred Value : 0.7143        
             Prevalence : 0.5186        
         Detection Rate : 0.4529        
   Detection Prevalence : 0.7700        
      Balanced Accuracy : 0.6073        
                                        
       'Positive' Class : 1             
                                        

Con ese punto de corte podemos ver que dada la clase 1 (asistió) los valores para las métricas en el dataset de testeo son:

  • Accuracy: 0.6171
  • Precision: 0.5881
  • Sensitibity: 0.8733
  • Specificity: 0.3412

Al compararlas con las métricas del data set de entrenamiento, se observa que dismibuyen levemente. Esto es así porque el modelo fue entrenado en el otro dataset (train) y esos mismos datos se utilizaron para elegir el punto de corte. Sin embargo, esta disminución no es llamativa. El Accuracy pasa de 0.6448 a 0.6171, la Precision de 0.6111 a 0.5881, la Sensitibity de 0.8970 a 0.8733 y la Specificity, de 0.3635 a 0.3412.

Los valores superiores a 0.85 de la métrica Sensitibity (una medida de la proporción de casos positivos reales que se pronosticaron como positivos, es decir, True Positive), indica que el modelo funcionaría relativamente bien para identificar a las personas que asistieron en el último año al cine, al teatro y/o a recitales.

Finalmente, tanto en el dataset de entrenamiento como el de testeo, se oberva que el accuracy es estadísticamente distinto a la clase mayoritaria (p-value [Acc > NIR] < 0.05).

Conclusiones

Retomamos la hipótesis de la que partimos: las mujeres con hijos y/o que conviven con personas mayores de 65 años van al cine, al teatro y/o a recitales en menor medida que los varones en la misma condición.

Luego de la aplicación de los modelos de regresión logística presentados, podríamos afirmar que las variables que tiene mayor influencia sobre la asitencia o no a este tipo de actividades culturales son:

  • la región del país: siendo las personas que viven en la Ciudad Autónoma de Buenos Aires las que tiene mayor probabilidad de haber asistido en el último año al cine, al teatro y/o a espectáculos de música en vivo.
  • la edad: quienes tienen mayor probabilidad de haber asistido al cine, al teatro y/o a recitales en el último año son los más jóvenes (entre 12 y 17 años). Al mirar los coeficientes, se observa que la probablidad de haber asistido a este tipo de eventos en el último año, disminuye a medida que la edad de la persona aumenta.
  • Principal sostén económico del hogar (PSE): las personas que no son el principal sostén económico de los hogares en los que viven tienen mayor probabilidad de haber asistido al cine, al teatro y/o a recitales durante el último año que las que sí lo son. Esto puede estar relacionado con lo obervado en el punto anterior: si quienes tienen mayor probabilidad de haber asistido a este tipo de actividades durante el último año son jóvenes entre 12 y 17, podría ser esperable que quienes tengan mayores probabilidades de asistencia no sean el principal sostén económico de los hogares en los que viven.

Si bien la variable convive_niñxs_adultosmayores no formó parte del modelo (múltiple) que logró minimizar la deviance, al ser analizada de forma individual, sí presentó una diferencia estadísticamente significativa: las personas que conviven con niñxs, adolecentes y/o adultos mayores tienen una probabilidad de asistencia al cine, teatro y/o recitales en el último año menor que las personas que no conviven. En línea con nuestra hipótesis incial, podría pensarse entonces que el hecho de convivir con menores y/o personas mayores de 65 años reduce la probabilidad de asistencia a este tipo de actividades. Queda pendiente para futuros trabajos analizar si dicha tendencia puede asociarse o no con tener a cargo tareas de cuidado.

Finalmente, la variable sexo no presentó diferencias significativas en ninguno de los modelos aplicados.

Consideramos que para futuras líneas de trabajo sería interesante plantear la exclusión de CABA como región analizada y/o replicar el análisis pero sólo sobre las personas que tienen entre 30 y 49 años, para observar qué ocurre con la influencia del resto de las variables en los modelos. También puede ser recomendable para futuras investigaciones incorporar variables relacionadas al nivel socioeconómico (NSE) de las personas entrevistadas.

LS0tDQp0aXRsZTogIkFwbGljYWNpw7NuIGRlIG1vZGVsb3MgZGUgUmVncmVzacOzbiBMb2fDrXN0aWNhIGVuIGxhIEVuY3Vlc3RhIE5hY2lvbmFsIGRlIENvbnN1bW9zIEN1bHR1cmFsZXMgMjAxNyINCmF1dGhvcjogIkYuIEF5ZWzDqW4gT3Bhem8geSBNYWdhbGkgUm9kcmlndWVzIFBpcmVzIg0KZGF0ZTogIjUgZGUgZGljaWVtYnJlIGRlIDIwMjEiDQpvdXRwdXQ6DQogIGh0bWxfbm90ZWJvb2s6DQogICAgdG9jOiB5ZXMNCiAgICB0b2NfZmxvYXQ6IHllcw0KICAgIGRmX3ByaW50OiBwYWdlZA0KICBodG1sX2RvY3VtZW50Og0KICAgIHRvYzogeWVzDQogICAgZGZfcHJpbnQ6IHBhZ2VkDQotLS0NCg0KPHN0eWxlIHR5cGU9InRleHQvY3NzIj4NCmRpdi5tYWluLWNvbnRhaW5lciB7DQogIG1heC13aWR0aDogMTYwMHB4Ow0KICBtYXJnaW4tbGVmdDogYXV0bzsNCiAgbWFyZ2luLXJpZ2h0OiBhdXRvOw0KfQ0KPC9zdHlsZT4NCg0KKipFbmZvcXVlIEVzdGFkw61zdGljbyBkZWwgQXByZW5kaXphamUgLyBUUDIgKioNCg0KIyMgUGxhbnRlbyBkZWwgUHJvYmxlbWENCg0KRWwgb2JqZXRpdm8gZ2VuZXJhbCBkZSBlc3RlIHRyYWJham8gZXMgZ2VuZXJhciB1bmEgc2VyaWUgZGUgbW9kZWxvcyBkZSByZWdyZXNpw7NuIHF1ZSBwZXJtaXRhbiAqKmV4cGxpY2FyIHkgcHJlZGVjaXIgbGEgYXNpc3RlbmNpYSBhbCB0ZWF0cm8sIGFsIGNpbmUgeS9vIGEgZXNwZWN0w6FjdWxvcyBkZSBtw7pzaWNhIGVuIHZpdm8gYSBwYXJ0aXIgZGUgZGV0ZXJtaW5hZGEgaW5mb3JtYWNpw7NuIHNvY2lvZGVtb2dyw6FmaWNhKiouIA0KDQpQYXJhIGVsbG8gdHJhYmFqYXJlbW9zIGNvbiBsb3MgZGF0b3MgcXVlIHByb3ZpZW5lbiBkZSBsYSBlZGljacOzbiAyMDE3IGRlIGxhICpFbmN1ZXN0YSBOYWNpb25hbCBkZSBDb25zdW1vcyBDdWx0dXJhbGVzKiwgZWxhYm9yYWRhIHBvciBlbCBTaXN0ZW1hIGRlIEluZm9ybWFjacOzbiBDdWx0dXJhbCBkZSBBcmdlbnRpbmEgKFNJbkNBKSBxdWUgcGVydGVuZWNlIGFsIE1pbmlzdGVyaW8gZGUgQ3VsdHVyYSBkZSBsYSBOYWNpw7NuLiBMYSBFTkNDIHNlIHJlYWxpemEgY2FkYSBjdWF0cm8gYcOxb3MgeSBidXNjYSBjb25vY2VyIGVuIHByb2Z1bmRpZGFkIGVsIGNvbXBvcnRhbWllbnRvIGRlIGxhIHBvYmxhY2nDs24gYXJnZW50aW5hIHJlc3BlY3RvIGRlIGxvcyBow6FiaXRvcyB5IGNvbnN1bW9zIGN1bHR1cmFsZXMuIEluZGFnYSBhY2VyY2EgZGUgbGFzIGZyZWN1ZW5jaWFzIGRlIGNvbnN1bW8sIGVsIGVxdWlwYW1pZW50byBjdWx0dXJhbCwgZWwgdGllbXBvIHByb21lZGlvIGRlIGNvbnN1bW8gbyBwcsOhY3RpY2EsIGVsIGdyYWRvIGRlIGRpZ2l0YWxpemFjacOzbiBkZSBsb3MgY29uc3Vtb3MgY3VsdHVyYWxlcywgbGFzIGZvcm1hcyBkZSByZWFsaXphY2nDs24gZGUgbG9zIGNvbnN1bW9zLCBsb3Mgc29wb3J0ZXMgdXRpbGl6YWRvcywgZW50cmUgb3Ryb3MgYXNwZWN0b3MuDQoNClNlZ8O6biBlbCBpbmZvcm1lICpNdWplcmVzIGVuIGxhIEN1bHR1cmEqIHB1YmxpY2FkbyBwb3IgZWwgU0luQ0EsIDggZGUgY2FkYSAxMCBwZXJzb25hcyBxdWUgcmVzcG9uZGllcm9uIHF1ZSBubyBhc2lzdGVuIGEgcmVjaXRhbGVzIHBvciDigJxtb3Rpdm9zIGZhbWlsaWFyZXMsIGNvbW8gdGVuZXIgaGlqb3MgcGVxdWXDsW9z4oCdLCBzb24gbXVqZXJlcy4gQWwgbWlzbW8gdGllbXBvLCBkZSBjYWRhIDEwIG11amVyZXMgcXVlIGZ1ZXJvbiBhIHJlY2l0YWxlcyBlbiAyMDE3LCAzIHNvbiBzb3N0w6luIGRlIGhvZ2FyIChlbnRyZSBsb3MgaG9tYnJlcyBsYSByZWxhY2nDs24gZXMgbcOhcyBwYXJlamE6IDUgZGUgY2FkYSAxMCkuDQoNClBhcnRpbW9zIGRlIGxhICpoaXDDs3Rlc2lzKiBkZSBxdWUgbGFzIG11amVyZXMgY29uIGhpanhzIHkvbyBxdWUgY29udml2ZW4gY29uIHBlcnNvbmFzIG1heW9yZXMgZGUgNjUgYcOxb3MgdmFuIGFsIGNpbmUsIGFsIHRlYXRybyB5IGEgcmVjaXRhbGVzIGVuIG1lbm9yIG1lZGlkYSBxdWUgbG9zIHZhcm9uZXMgZW4gbGEgbWlzbWEgY29uZGljacOzbi4gIEVuIGVzdGUgc2VudGlkbywgYnVzY2Ftb3MgcHJlZGVjaXIgc2kgdW5hIHBlcnNvbmEgYXNpc3Rpw7MgZW4gZWwgw7psdGltbyBhw7FvIG8gbm8gYSBhbGd1bmEgZGUgbGFzIHRyZXMgYWN0aXZpZGFkZXMgbWVuY2lvbmFkYXMsIGEgcGFydGlyIGRlIHN1cyBjYXJhY3RlcsOtc3RpY2FzIHNvY2lvZGVtb2dyw6FmaWNhcy4gVXRpbGl6YXJlbW9zIG1vZGVsb3MgZGUgICpyZWdyZXNpw7NuIGxvZ8Otc3RpY2EqIHlhIHF1ZSBzb24gbG9zIG3DoXMgZWZpY2llbnRlcyBwYXJhIHByb2JsZW1hcyBkZSBwcmVkaWNjacOzbiBkZSBjbGFzZXMuDQoNCmBgYHtyfQ0KbWVzc2FnZSA9IEZBTFNFDQpgYGANCg0KDQpgYGB7cn0NCiMgQ2FyZ2Ftb3MgbGlicmVyw61hcw0KDQpsaWJyYXJ5KHJlYWRyKQ0KbGlicmFyeSh0aWR5dmVyc2UpDQpsaWJyYXJ5KHRpZHltb2RlbHMpDQpsaWJyYXJ5KG1vZGVscikNCmxpYnJhcnkoR0dhbGx5KQ0KbGlicmFyeShwUk9DKQ0KbGlicmFyeShjb3dwbG90KQ0KbGlicmFyeShPbmVSKQ0KbGlicmFyeShybGFuZykNCmxpYnJhcnkoY2FyZXQpDQpsaWJyYXJ5KGphbml0b3IpDQpsaWJyYXJ5KGNvcnJyKQ0KbGlicmFyeShrbml0cikNCmxpYnJhcnkoa2FibGVFeHRyYSkNCmxpYnJhcnkoZ3JpZEV4dHJhKQ0KYGBgDQoNCiMjIEV4cGxvcmFjacOzbiBkZSBsb3MgZGF0b3MNCg0KRW4gZXN0ZSBwcmltZXIgYXBhcnRhZG8gY29uZmVjY2lvbmFtb3MgZWwgZGF0YXNldCBjb24gZWwgcXVlIHZhbW9zIGEgdHJhYmFqYXIsIGEgcGFydGlyIGRlbCBkYXRhc2V0IG9yaWdpbmFsIGRlIGxhIEVuY3Vlc3RhIE5hY2lvbmFsIGRlIENvbnN1bW9zIEN1bHR1cmFsZXMuIFRhbWJpw6luIGxsZXZhbW9zIGEgY2FibyB1bmEgZXhwbG9yYWNpw7NuIGluaWNpYWwgZGUgbGFzIGRpc3RyaWJ1Y2lvbmVzIGRlIGxvcyBkYXRvcywgYSBwYXJ0aXIgZGUgbGEgYXBlcnR1cmEgcG9yIGxhIHZhcmlhYmxlIHRhcmdldC4gDQoNCiMjIyBDb25mZWNjacOzbiBkZWwgZGF0YXNldCBkZSB0cmFiYWpvIHkgYW7DoWxpc2lzIGRlIHN1IGVzdHJ1Y3R1cmENCg0KRWwgZGF0YXNldCBjb21wbGV0byBkZSBsYSBFbmN1ZXN0YSBOYWNpb25hbCBkZSBDb25zdW1vcyBDdWx0dXJhbGVzIHRpZW5lIDI4MDIgb2JzZXJ2YWNpb25lcyAocmVzcHVlc3RhcykgeSA0NTAgY29sdW1uYXMgKHF1ZSByZWZpZXJlbiBhIGxhcyAxMTcgcHJlZ3VudGFzIGRlbCBjdWVzdGlvbmFyaW8pLiBUb21hbmRvIGNvbW8gZ3XDrWEgbGEgaGlww7N0ZXNpcyBkZSB0cmFiYWpvLCBhcm1hbW9zIHVuYSB2ZXJzacOzbiByZXN1bWlkYSBkZWwgZGF0YXNldCBlbiBsYSBxdWUgY29uc2VydmFyZW1vcyBzb2xvIGxhcyB2YXJpYWJsZXMgcXVlIHJlc3VsdGFuIHBlcnRpbmVudGVzIGFsIHByb2JsZW1hLiANCg0KYGBge3J9DQojY2FyZ2Ftb3MgZWwgZGF0YXNldCBvcmlnaW5hbA0KY29uc3Vtb3MgPC0gcmVhZC5jc3YoIkM6L1VzZXJzL21hZ2FsL09uZURyaXZlL0VzY3JpdG9yaW8vRUVBL3RwMi9lbmNjMTcvZW5jY18yMDE3LmNzdiIsIGVuY29kaW5nID0gJ1VURi04JykNCg0KI2FybWFtb3MgbGEgdmVyc2nDs24gcmVzdW1pZGEgY29uIGxhIHF1ZSB2YW1vcyBhIHRyYWJhamFyDQpjb25zdW1vcyA8LSBkYXRhLmZyYW1lKGNvbnN1bW9zWyxjKDEsMiw0OjYsNDAyLDQwMyw0MDYsODIsODMsODUsODYsMjA1LDIwNiwyMDgsMjA5LDIyNywyMjgpXSkNCg0KI3Zpc3VhbGl6YW1vcyBzdSBlc3RydWN0dXJhDQpnbGltcHNlKGNvbnN1bW9zKQ0KDQpgYGANCmBgYHtyfQ0KI2VsaW1pbmFtb3MgZmlsYSBjb24gZXJyb3IgZGUgY2FyZ2ENCmNvbnN1bW9zID0gY29uc3Vtb3NbLTI3NDAsXQ0KYGBgDQpgYGB7cn0NCiNlbGltaW5hbW9zIGZpbGEgY29uIGVycm9yIGRlIGNhcmdhDQpjb25zdW1vcyA9IGNvbnN1bW9zWy0xOTUwLF0NCmBgYA0KDQpMYXMgKnZhcmlhYmxlcyogZGVsIHNldCBpbmNsdXllbiBpZCwgKipzZXhvKiogZGUgbGEgcGVyc29uYSBlbmN1ZXN0YWRhLCAqKmVkYWQqKiwgKipyZWdpw7NuKiogZGUgcmVzaWRlbmNpYSwgYXPDrSBjb21vIHRhbWJpw6luOg0KDQotIHAxMDRfMTogwr9DdcOhbnRhcyBwZXJzb25hcyAoY29uIGxhIHF1ZSBjb252aXZlKSBzb24gKiptZW5vcmVzIGRlIDE1IGHDsW9zKio/DQotIHAxMDRfMjogwr9DdcOhbnRhcyBwZXJzb25hcyAoY29uIGxhIHF1ZSBjb252aXZlKSBzb24gKiptYXlvcmVzIGRlIDY1IGHDsW9zKio/DQotIHAxMDc6IMK/UG9kcsOtYSBkZWNpcm1lIGN1w6FsIGVzIGxhIHBlcnNvbmEgcXVlIG3DoXMgYXBvcnRhIGEgbG9zIGdhc3RvcyBkZWwgaG9nYXIsIGVzIGRlY2lyLCBlbCAqKnByaW5jaXBhbCBzb3N0w6luIGVjb27Ds21pY28gZGVsIGhvZ2FyKiogKFBTSCk/IA0KLSBwMjM6IMK/Q29uY3VycmnDsyBhIHJlY2l0YWxlcyBvIHByZXNlbnRhY2lvbmVzIGRlICoqbcO6c2ljYSBlbiB2aXZvKiogZHVyYW50ZSBlbCDDumx0aW1vIGHDsW8/IA0KLSBwMjNfMSBbc29sbyBwYXJhIHF1aWVuZXMgcmVzcG9uZGVuIE5PIGVuIHAyM106IMK/Q3XDoWwgZXMgZWwgbW90aXZvIHByaW5jaXBhbCBwb3IgZWwgcXVlIG5vIGNvbmN1cnJlIGEgcmVjaXRhbGVzPw0KLSBwMjNfMiBbc29sbyBwYXJhIHF1aWVuZXMgcmVzcG9uZGVuIE5PIGVuIHAyM106IEFudGVyaW9ybWVudGUsIMK/QWNvbnN0dW1icmFiYSBhIGlyIGEgcmVjaXRhbGVzPw0KLSBwMjQgW3NvbG8gcGFyYSBxdWllbmVzIHJlc3BvbmRlbiBTSSBlbiBwMjNdOiDCv0NvbiBxdcOpIGZyZWN1ZW5jaWEgY29uY3VycmnDsyBhIHJlY2l0YWxlcyBlbCDDumx0aW1vIGHDsW8/DQotIHA2Mzogwr9Db25jdXJyacOzIGFsICoqY2luZSoqIGVsIMO6bHRpbW8gYcOxbz8NCi0gcDYzXzEgW3NvbG8gcGFyYSBxdWllbmVzIHJlc3BvbmRlbiBOTyBlbiBwNjNdOiDCv0N1w6FsIGVzIGVsIG1vdGl2byBwcmluY2lwYWwgcG9yIGVsIHF1ZSBubyBjb25jdXJyZSBhbCBjaW5lPw0KLSBwNjNfMiBbc29sbyBwYXJhIHF1aWVuZXMgcmVzcG9uZGVuIE5PIGVuIHA2M106IEFudGVyaW9ybWVudGUsIMK/QWNvbnN0dW1icmFiYSBhIGlyIGFsIGNpbmU/DQotIHA2NCBbc29sbyBwYXJhIHF1aWVuZXMgcmVzcG9uZGVuIFNJIGVuIHA2M106IMK/Q29uIHF1w6kgZnJlY3VlbmNpYSBjb25jdXJyacOzIGFsIGNpbmUgZWwgw7psdGltbyBhw7FvPw0KLSBwNjg6IMK/Q29uY3VycmnDsyBhICoqZXNwZWN0w6FjdWxvcyB0ZWF0cmFsZXMqKiBlbCDDumx0aW1vIGHDsW8/DQotIHA2OSBbc29sbyBwYXJhIHF1aWVuZXMgcmVzcG9uZGVuIFNJIGVuIHA2OF06IMK/Q29uIHF1w6kgZnJlY3VlbmNpYSBjb25jdXJyacOzIGEgZXNwZWN0w6FjdWxvcyB0ZWF0cmFsZXMgZWwgw7psdGltbyBhw7FvPw0KDQoNClNlIG9ic2VydmEgcXVlIGxhcyB2YXJpYWJsZXMgc2VsZWNjaW9uYWRhcyBwdWVkZW4gYWdydXBhcnNlIGVuIGRvcyBibG9xdWVzOiBhcXVlbGxhcyByZWxhY2lvbmFkYXMgY29uIGxhcyAqcHLDoWN0aWNhcyBjdWx0dXJhbGVzKiBkZSBsYXMgcGVyc29uYXMgZW50cmV2aXN0YWRhcyB5IGxhcyByZWxhY2lvbmFkYXMgY29uIHN1ICppbmZvcm1hY2nDs24gc29jaW9kZW1vZ3LDoWZpY2EqLg0KDQpMYSAqcHJlZ3VudGEgMTA0KiBmdWUgaW5jbHXDrWRhIHBvcnF1ZSBub3MgaW50ZXJlc2EgYW5hbGl6YXIsIGVudHJlIG90cmFzIGNvc2FzLCBzaSBlbCBoZWNobyBkZSBjb252aXZpciBjb24gbmnDsXhzLCBhZG9sZWNlbnRlcyB5L28gYWR1bHRvcyBtYXlvcmVzIGVzdMOhIHJlbGFjaW9uYWRvIGNvbiBsYSBmcmVjdWVuY2lhIGRlIGFzaXN0ZW5jaWEgYSBwcsOhY3RpY2FzIGN1bHR1cmFsZXMgZGUgbGEgcGVyc29uYSBlbnRyZXZpc3RhZGEsIHBhcnRpZW5kbyBkZSBsYSBoaXDDs3Rlc2lzIGRlIHF1ZSB0ZW5lciBhIGNhcmdvIHRhcmVhcyBkZSBjdWlkYWRvIHB1ZWRlIGludGVyZmVyaXIgZW4gbGEgIGNvbmN1cnJlbmNpYSBhIGVzdGUgdGlwbyBkZSBhY3RpdmlkYWRlcy4gRW4gZXN0ZSBzZW50aWRvLCBub3MgaW50ZXJlc2EgYW5hbGl6YXIgc2kgbGEgcmVsYWNpw7NuIGVudHJlIGFtYmFzIHZhcmlhYmxlcyBlcyBzaW1pbGFyIG8gbm8gZW50cmUgdmFyb25lcyB5IG11amVyZXMuIA0KDQoqQ3JlYWNpw7NuIGRlIGxhIHZhcmlhYmxlIGRpY290w7NtaWNhICJjb252aXZlX25pw7F4c19hZHVsdG9zbWF5b3JlcyIqLiBMYXMgdmFyaWFibGVzIG9yaWdpbmFsZXMgZGUgbGEgcHJlZ3VudGEgMTA0IHJlZ2lzdHJhbiBsYSBjYW50aWRhZCBkZSBtZW5vcmVzIGRlIDE1IGHDsW9zIG8gbWF5b3JlcyBkZSA2NSBhw7FvcyBxdWUgY29udml2ZW4gY29uIGxhIHBlcnNvbmEgZW50cmV2aXN0YWRhLiBTZSBkaWNvdG9taXphcm9uIGFtYmFzIHZhcmlhcmJsZXMgeSBzZSBjcmXDsyB1bmEgbnVldmEgcXVlIHRvbWEgdmFsb3IgMSBzaSBsYSBwZXJzb25hIGVudHJldmlzdGFkYSAqKmNvbnZpdmUgY29uIGFsIG1lbm9zIHVuIG1lbm9yIGRlIDE1IGHDsW9zIHkvbyBhbCBtZW5vcyB1biBtYXlvciBkZSA2NSoqLg0KDQoNCmBgYHtyfQ0KI3NlIHJlZW1wbGF6YW4gbG9zIHZhbG9yZXMgTkEgcG9yIDAgKHJlZmllcmVuIGEgbGFzIHBlcnNvbmFzIHF1ZSB2aXZlbiBzb2xhcyB5IG5vIHJlc3BvbmRpZXJvbiBlc3RhcyBwcmVndW50YXMpDQpjb25zdW1vcyA8LSBtdXRhdGVfYXQoY29uc3Vtb3MsIGMoInAxMDRfMSIsICJwMTA0XzIiKSwgfnJlcGxhY2UoLiwgaXMubmEoLiksIDApKQ0KDQojU2UgZGljb3RvbWl6YW4gbGFzIHZhcmliYWxlcyBwMTA0XzEgLyBwMTA0XzINCmNvbnN1bW9zJHAxMDRfMV9SRUNBVCA8LSBpZmVsc2UoY29uc3Vtb3MkcDEwNF8xICE9IDAsIDEsIDApDQpjb25zdW1vcyRwMTA0XzJfUkVDQVQgPC0gaWZlbHNlKGNvbnN1bW9zJHAxMDRfMiAhPSAwLCAxLCAwKQ0KDQojc2UgY3JlYSB1bmEgbnVldmEgY29sdW1uYSBxdWUgc2UgbGxhbWEg4oCcY29udml2ZV9uacOxeHNfYWR1bHRvc21heW9yZXPigJ0geSB0b21hIHZhbG9yZXMgMSBzaSBsYSBwZXJzb25hIGVuY3Vlc3RhZGEgdml2ZSBjb24gbWVub3JlcyBkZSAxNSBhw7FvcyB5L28gbWF5b3JlcyBkZSA2NQ0KY29uc3Vtb3MgPC0gY29uc3Vtb3MgJT4lIA0KICBtdXRhdGUoY29udml2ZV9uacOxeHNfYWR1bHRvc21heW9yZXMgPSBjYXNlX3doZW4ocDEwNF8xX1JFQ0FUID09IDEgfCBwMTA0XzJfUkVDQVQgPT0gMSB+IDEpKQ0KDQojc2UgcmVlbXBsYXphbiBsb3MgdmFsb3JlcyBOQSBwb3IgMA0KY29uc3Vtb3MgPC0gbXV0YXRlX2F0KGNvbnN1bW9zLCBjKCJjb252aXZlX25pw7F4c19hZHVsdG9zbWF5b3JlcyIpLCB+cmVwbGFjZSguLCBpcy5uYSguKSwgMCkpDQpgYGANCg0KYGBge3J9DQojIyBMYSB0cmFuc2Zvcm1hbW9zIGVuIHVuYSB2YXJpYWJsZSBjYXRlZ8OzcmljYQ0KY29uc3Vtb3MgPSBjb25zdW1vcyAlPiUgDQogIG11dGF0ZShjb252aXZlX25pw7F4c19hZHVsdG9zbWF5b3JlcyA9IGNhc2Vfd2hlbihjb252aXZlX25pw7F4c19hZHVsdG9zbWF5b3Jlcz09IDEgfiAiU0kiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbnZpdmVfbmnDsXhzX2FkdWx0b3NtYXlvcmVzPT0gMCB+ICJOTyIpKQ0KYGBgDQoNCg0KKkNyZWFjacOzbiBkZSBsYSB2YXJpYWJsZSBjYXRlZ8OzcmljYSAiZnJhbmphX2V0YXJpYSIqLiBMYSB2YXJpYWJsZSBlZGFkIGVuIGVsIGRhdGFzZXQgb3JpZ2luYWwgZXMgdW5hIHZhcmlhYmxlIGNvbnRpbnVhLiBBcGxpY2Ftb3MgZWwgbWlzbW8gY3JpdGVyaW8gZGUgY29uc3RydWNjacOzbiBkZSBmcmFuamFzIGV0YXJpYXMgcXVlIGVsIFNpc3RlbWEgZGUgSW5mb3JtYWNpw7NuIEN1bHR1cmFsIGRlIEFyZ2VudGluYSAoU0luQ0EpIHkgbGEgZGlzY3JldGl6YW1vcy4gDQoNCmBgYHtyfQ0KI3RyYW5zZm9ybWFtb3MgbGEgdmFyaWFibGUgZWRhZCBlbiBudW3DqXJpY2ENCmNvbnN1bW9zJGVkYWQgPC0gYXMubnVtZXJpYyhjb25zdW1vcyRlZGFkKQ0KDQojY3JlYW1vcyBsYSB2YXJpYWJsZSBmcmFuamFfZXRhcmlhDQpjb25zdW1vcyRmcmFuamFfZXRhcmlhID0gY3V0KGNvbnN1bW9zJGVkYWQsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBicmVha3MgPSBjKDEyLDE3LDI5LDQ5LDY0LDkyKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxhYmVscyA9IGMoImRlMTJhMTciLCJkZTE4YTI5IiwgImRlMzBhNDkiLCAiZGU1MGE2NCIsICI2NW9tw6FzIikpDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIA0KYGBgDQoNCg0KIyMjIERlZmluaW1vcyBudWVzdHJhIHZhcmlhYmxlIHRhcmdldDogYXNpc3RlbmNpYSBhbCBjaW5lLCBhbCB0ZWF0cm8geS9vIGEgdW4gZXNwZWN0w6FjdWxvIG11c2ljYWwgZW4gZWwgw7psdGltbyBhw7FvDQoNCkxvIHF1ZSBub3MgaW50ZXJlc2EgcHJlZGVjaXIgZXMgc2kgbGEgcGVyc29uYSBlbnRyZXZpc3RhZGEgZnVlIGFsIGNpbmUsIGFsIHRlYXRybyB5L28gYSB1biByZWNpdGFsIGVuIHZpdm8gZWwgw7psdGltbyBhw7FvLCBhIHBhcnRpciBkZSBzdXMgY2FyYWN0ZXLDrXN0aWNhcyBzb2Npb2RlbW9ncsOhZmljYXMuIEFncnVwYW1vcyBlc3RhcyB0cmVzIHZhcmlhYmxlcyBlbiB1bmEgbnVldmEgZGljb3TDs21pY2EgKioiYXNpc3RlbmNpYSIqKiwgcXVlIGV4cHJlc2EgMT0gIlPDrSwgYXNpc3Rpw7MgYWwgbWVub3MgYSB1bm8gZGUgZXN0b3MgZXZlbnRvcyBlbiBlbCDDumx0aW1vIGHDsW8iIG8gMD0gIk5vIGFzaXN0acOzIGEgbmluZ3VubyIuIEEgY29udGludWFjacOzbiwgYW5hbGl6YW1vcyBzdXMgZGlzdHJpYnVjaW9uZXMgcG9yIHNlcGFyYWRvIHkgbGEgcmVsYWNpw7NuIGNvbiBlbCByZXN0byBkZSBsYXMgdmFyaWFibGVzLiANCg0KYGBge3J9DQpjb25zdW1vcyAlPiUgdGFieWwocDIzKSU+JSAjYXNpc3Rpw7MgYSByZWNpdGFsZXMNCiAgYXJyYW5nZShkZXNjKHBlcmNlbnQpLCBuKSU+JQ0KICBmYXNoaW9uKCkNCmNvbnN1bW9zICU+JSB0YWJ5bChwNjMpJT4lICNhc2lzdGnDsyBhIGNpbmVzDQogIGFycmFuZ2UoZGVzYyhwZXJjZW50KSwgbiklPiUNCiAgZmFzaGlvbigpDQpjb25zdW1vcyAlPiUgdGFieWwocDY4KSU+JSAjYXNpc3Rpw7MgYSB0ZWF0cm9zDQogIGFycmFuZ2UoZGVzYyhwZXJjZW50KSwgbiklPiUNCiAgZmFzaGlvbigpDQpgYGANCg0KVmVtb3MgcXVlIGhheSBtYXlvciBkZXNiYWxhbmNlIGRlIGNsYXNlcyBlbiBsYSBhc2lzdGVuY2lhIHRlYXRyb3MsIHNlZ3VpZG8gcG9yIGFzaXN0ZW5jaWEgYSByZWNpdGFsZXMgeSwgZmluYWxtZW50ZSwgYSBsb3MgY2luZXMuDQoNCg0KYGBge3J9DQojR2VuZXJhbW9zIGxhIG51ZXZhIHZhcmlhYmxlDQpjb25zdW1vcyA8LSBjb25zdW1vcyAlPiUgDQogIG11dGF0ZShhc2lzdGVuY2lhID0gY2FzZV93aGVuKHAyMz09IlNJIiB8ICBwNjg9PSJTSSIgfCAgcDYzPT0iU0kiIH4gMSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRSVUUgfiAwKSkNCg0KYGBgDQoNCmBgYHtyfQ0KI29ic2VydmFtb3Mgc3UgZGlzdHJpYnVjacOzbg0KY29uc3Vtb3MgJT4lIHRhYnlsKGFzaXN0ZW5jaWEpJT4lIA0KICBhcnJhbmdlKGRlc2MocGVyY2VudCksIG4pJT4lDQogIGZhc2hpb24oKQ0KYGBgDQoNCkVsIDUyJSBkZSBsYXMgcGVyc29uYXMgZW50cmV2aXN0YWRhcyBhc2lzdGnDsyBhbCBjaW5lLCBhbCB0ZWF0cm8geS9vIGEgdW4gZXNwZWN0w6FjdWxvIGRlIG3DunNpY2EgZW4gdml2byBlbCDDumx0aW1vIGHDsW8uIE1pZW50cmFzIHF1ZSBlbCA0NyUgbm8gbG8gaGl6by4gDQoNCg0KYGBge3IsIG1lc3NhZ2U9RkFMU0UsIGZpZy53aWR0aD0xMiwgZmlnLmhlaWdodD02fQ0KDQojIGdyYWZpY2Ftb3MgY29uIGdncGFpcnMgY29sb3JlYW5kbyBwb3IgdmFyaWFibGUgYSBwcmVkZWNpcg0KZyA8LSBjb25zdW1vcyAlPiUgDQogICAgICAgIHNlbGVjdCgiYXNpc3RlbmNpYSIsInNleG8iLCJyZWdpb24iLCAiZnJhbmphX2V0YXJpYSIsICJwMTA3IiwgImNvbnZpdmVfbmnDsXhzX2FkdWx0b3NtYXlvcmVzIikgJT4lIA0KICAgICAgICBnZ3BhaXJzKHRpdGxlID0gIkFwZXJ0dXJhIHBvciB2YXJpYWJsZSB0YXJnZXQ6IGFzaXN0ZW5jaWEgYWwgY2luZSwgYWwgdGVhdHJvIHkvbyBhIHJlY2l0YWxlcyBlbiBlbCDDumx0aW1vIGHDsW8iLA0KICAgICAgICAgICAgICAgIG1hcHBpbmcgPSBhZXMoY29sb3VyPSBmYWN0b3IoYXNpc3RlbmNpYSkpLA0KICAgICAgICAgICAgICAgIHByb2dyZXNzID0gRkFMU0UsIA0KICAgICAgICAgICAgICAgIGxvd2VyPWxpc3QoY29tYm89d3JhcCgiZmFjZXRoaXN0IiwgYmlud2lkdGg9MC44KSksIGxlZ2VuZCA9IDI1KSArDQogICAgICAgIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTAsIGhqdXN0ID0gMSkpICsgDQogICAgICAgIHRoZW1lX2J3KCkgKw0KICAgICAgICBzY2FsZV9maWxsX2JyZXdlcihwYWxldHRlPSJTZXQxIikgKw0KICAgICAgICBzY2FsZV9jb2xvcl9icmV3ZXIocGFsZXR0ZT0iU2V0MSIpDQpnDQpgYGANCg0KQWwgcmVhbGl6YXIgbGEgYXBlcnR1cmEgcG9yIGxhIHZhcmlhYmxlIHRhcmdldCwgZW4gZWwgYW7DoWxpc2lzIGdyw6FmaWNvIHNlIG9ic2VydmEgcXVlIGxhIGNsYXNlICphc2lzdGVuY2lhKiBlcyBmYWNpbG1lbnRlIGRpc2NyaW1pbmFibGUsIGFtYmFzIGNhdGVnb3LDrWFzIHNlIHNlcGFyYW4gY29uIGJhc3RhbnRlIGNsYXJpZGFkLiBSZWNvcmRlbW9zIHF1ZSBsYXMgY2xhc2VzIGVzdGFuIGJhc3RhbnRlIGJhbGFuY2VhZGFzLg0KDQpWZW1vcyBxdWUgcGFyYSBsYSB2YXJpYWJsZSAqc2V4byogbGFzIGRpc3RyaWJ1Y2lvbmVzIGRlIGFzaXN0ZW5jaWEgc29uIHByb3BvcmNpb25hbGVzIHRhbnRvIHBhcmEgbXVqZXJlcyBjb21vIHBhcmEgdmFyb25lcywgc2llbmRvIGRlbCB0b3RhbCBkZSBtdWplcmVzIGVuY3Vlc3RhZGFzIGxhIG1pdGFkLCBhcHJveGltYWRhbWVudGUsIGxhcyBxdWUgYXNpc3RlbiBhIGV2ZW50b3MgZGUgZXN0ZSB0aXBvOyBhbCBpZ3VhbCBxdWUgcGFyYSBlbCBjYXNvIGRlIGxvcyB2YXJvbmVzOiBhcGVuYXMgdW4gcG9jbyBtw6FzIGRlIGxhIG1pdGFkIGRlIGxvcyBlbmN1ZXN0YWRvcyBhc2lzdGllcm9uIGVuIGVsIMO6bHRpbW8gYcOxbyBhbCBjaW5lLCBhbCB0ZWF0cm8geS9vIGEgcmVjaXRhbGVzLg0KDQpQYXJhIGVsIGNhc28gZGUgbGEgdmFyaWFibGUgcXVlIGNyZWFtb3MgKmNvbnZpdmVfbmnDsXhzX2FkdWx0b3NtYXlvcmVzKiBvY3VycmUgYWxnbyBzaW1pbGFyIHF1ZSBwYXJhIGxhIHZhcmlhYmxlICpzZXhvKjogZGUgbG9zIHF1ZSBzw60gY29udml2ZW4gY29uIG5pw7F4cywgYWRvbGVjZW50ZXMgeS9vIGFkdWx0b3MgbWF5b3JlcyAocXVlIHNvbiBsYSBtYXlvcsOtYSBkZSBsYXMgcGVyc29uYXMgZW5jdWVzdGFkYXMpIGFwcm94aW1hZGFtZW50ZSBsYSBtaXRhZCBzw60gYXNpc3Rpw7MgYSBldmVudG9zIGVuIGVsIMO6bHRpbW8gYcOxbyB5IGxhIG90cmEgbWl0YWQgbm8gYXNpc3Rpw7MsIG1pZW50cmFzIHF1ZSBkZW50cm8gZGUgbG9zIHF1ZSBubyBjb252aXZlbiBjb24gbmnDsXhzIG8gYWR1bHRvcyBtYXlvcmVzLCBlcyB1biBwb2NvIG3DoXMgbGEgY2FudGlkYWQgZGUgbG9zIHF1ZSBzw60gYXNpc3Rlbi4NClZlYW1vcyBhaG9yYSBlbCBjYXNvIGRlIGxhcyBwZXJzb25hcyBxdWUgY29udmllbiBjb24gbmnDsXhzLCBhZG9sZWNlbnRlcyB5L28gYWR1bHRvcyBtYXlvcmVzIHF1ZSBhc2lzdGllcm9uIGEgZXNwZWN0w6FjdWxvcyBkdXJhbnRlIGVsIMO6bHRpbW8gYcOxbyB2cy4gbG9zIHF1ZSBubyBsbyBoaWNpZXJvbiwgc2Vnw7puIHNleG86IG51ZXZhbWVudGUgc2Ugb2JzZXJ2YSBxdWUgaGF5IHVuYSBkaXN0cmlidWNpw7NuIGJhc3RhbnRlIHBhcmVqYSBlbnRyZSBtdWplcmVzIHkgdmFyb25lcy4NCg0KUG9yIG90cm8gbGFkbywgc2kgb2JzZXJ2YW1vcyBjb24gbWF5b3IgZGV0YWxsZSwgbGFzIHZhcmlhYmxlcyAqZnJhbmphX2V0YXJpYSogeSAqcmVnacOzbiBkZSByZXNpZGVuY2lhKiBzb24gbGFzIHF1ZSBwcmVzZW50YW4gbWF5b3JlcyB2YXJpYWNpb25lcy4gVGFtYmnDqW4gaGF5IG1heW9yIHZhcmlhYmlsaWRhZCBlbiBsYXMgdmFyaWFibGUgKnAxMDcqLCBwcmluY2lwYWwgc29zdMOpbiBlY29uw7NtaWNvIChQU0UpLiBMbyBjdWFsIHJlc3VsdGEgaW50ZXJlc2FudGUgcGFyYSBhbmFsaXphciBhZGljaW9uYWxtZW50ZSBlbiBsb3MgbW9kZWxvcyBxdWUgc2lndWVuLiAgDQoNCg0KRmluYWxtZW50ZSwgdmlzdWFsaXphbW9zIGxhcyBwcmltZXJhcyBzZWlzIGZpbGFzIGRlIG51ZXN0cm8gZGF0YXNldCBkZSB0cmFiYWpvIGRlZmluaXRpdm8uDQoNCmBgYHtyfQ0KaGVhZChjb25zdW1vcykNCmBgYA0KDQoNCiMjIE1vZGVsbyBkZSBSZWdyZXNpw7NuIExvZ8Otc3RpY2ENCg0KRW4gZXN0ZSBhcGFydGFkbyBjcmVhbW9zIHVuYSBzZXJpZSBkZSBtb2RlbG9zIHNpbXBsZXMgeSBtw7psdGlwbGVzIGRlIHJlZ3Jlc2nDs24gbG9nw61zdGljYSBlIGludGVycHJldGFtb3Mgc3VzIGNvZWZpY2llbnRlcy4gDQoNClF1ZXJlbW9zIGVzdGltYXIgKipQKEFzaXN0acOzPTF8WCk9UChYKSoqIHBhcmEgY2FkYSBpbmRpdmlkdW8geSBhIHBhcnRpciBkZSBlbGxvIHBvZGVyIGRlZmluaXIgdW4gcHVudG8gZGUgY29ydGUgcGFyYSBwcmVkZWNpciBxdWnDqW5lcyBhc2lzdGllcm9uIGFsIGNpbmUsIGFsIHRlYXRybyB5L28gYSBlc3BlY3TDoWN1bG9zIGRlIG3DunNpY2EgZW4gdml2byBlbCDDumx0aW1vIGHDsW9zIHkgcXVpZW5lcyBuby4NCg0KIyMjIFBhcnRpY2nDs24gZGVsIGRhdGFzZXQgZW4gKlRlc3QqIHkgKlRyYWluKg0KDQpQYXJhIGV2YWx1YXIgbG9zIG1vZGVsb3MgdmFtb3MgYSByZWFsaXphciB1bmEgcGFydGljacOzbiBlbnRyZSBkYXRhc2V0IGRlIGVudHJlbmFtaWVudG8gKDc1JSkgeSB0ZXN0ZW8gKDI1JSkgdXNhbmRvIGxhIGZ1bmNpw7NuICppbml0aWFsX3NwbGl0KiBkZWwgcGFxdWV0ZSByc2FtcGxlIGRlIHRpZHltb2RlbHMuIEVsIGRhdGFzZXQgZGUgZW50cmVuYW1pZW50byBxdWVkYXLDoSBjb24gdW4gdG90YWwgZGUgMjEwMCBjYXNvcyB5IGVsIGRlIHRlc3RlbywgY29uIDcwMC4NCg0KYGBge3J9DQojIEZpamFtb3Mgc2VtaWxsYQ0Kc2V0LnNlZWQoMjAyMSkNCg0KIyBQYXJ0aWNpw7NuIFRyYWluIHkgVGVzdCwgaW5kaWNhbmRvIHByb3BvcmNpw7NuDQp0cmFpbl90ZXN0IDwtIGluaXRpYWxfc3BsaXQoY29uc3Vtb3MsIHByb3AgPSAwLjc1KQ0KdHJhaW5fZGF0YSA8LSB0cmFpbmluZyh0cmFpbl90ZXN0KQ0KdGVzdF9kYXRhIDwtIHRlc3RpbmcodHJhaW5fdGVzdCkNCg0KIyBWZW1vcyBsYXMgZGltZW5zaW9uZXMgZGUgY2FkYSBwYXJ0aWNpb24NCnRyYWluX2RhdGEgJT4lDQogIGRpbV9kZXNjKCkgDQp0ZXN0X2RhdGEgJT4lDQogIGRpbV9kZXNjKCkNCmBgYA0KDQpSZXN1bHRhIHBlcnRpbmVudGUgYW5hbGl6YXIgbGFzICoqZGlzdHJpYnVjaW9uZXMgZGUgbGEgdmFyaWFibGUgdGFyZ2V0KiogZW4gYW1ib3MgZGF0YXNldHMuDQoNCmBgYHtyfQ0KIyBjYWxjdWxhbW9zIGxhIGRpc3RyaWJ1Y2nDs24gZGUgY2xhc2UgZW4gY2FkYSBkYXRhc2V0DQp0cmFpbiA8LSB0cmFpbl9kYXRhICU+JSANCiAgZ3JvdXBfYnkoYXNpc3RlbmNpYSkgJT4lIA0KICBzdW1tYXJpc2UobnVtZXJvX2Nhc29zPW4oKSkgJT4lDQogIG11dGF0ZShwcm9wID0gcm91bmQocHJvcC50YWJsZShudW1lcm9fY2Fzb3MpKjEwMCwyKSkNCnRlc3QgPC0gdGVzdF9kYXRhICU+JSANCiAgZ3JvdXBfYnkoYXNpc3RlbmNpYSkgJT4lIA0KICBzdW1tYXJpc2UobnVtZXJvX2Nhc29zPW4oKSkgJT4lDQogIG11dGF0ZShwcm9wID0gcm91bmQocHJvcC50YWJsZShudW1lcm9fY2Fzb3MpKjEwMCwyKSkNCg0KIyBhcm1hbW9zIHVuYSB0YWJsYSBjb25qdW50YSBwYXJhIGdyYWZpY2FyDQpkaXN0cmliID0gY2JpbmQocmJpbmQodHJhaW4sIHRlc3QpLCBkYXRhc2V0ID0gYygidHJhaW4iLCAidHJhaW4iLCAidGVzdCIsICJ0ZXN0IikpDQpkaXN0cmliDQoNCiMgZ3JhZmljYW1vcyBsYXMgZGlzdHJpYnVjaW9uZXMNCmdncGxvdChkaXN0cmliLCBhZXMoeCA9IGFzaXN0ZW5jaWEsIHkgPSBwcm9wLCBmaWxsID0gZmFjdG9yKGFzaXN0ZW5jaWEpLCBsYWJlbCA9IHByb3ApKSArIA0KICBnZW9tX2JhcihzdGF0PSJpZGVudGl0eSIsIHBvc2l0aW9uID0gImRvZGdlIikgKyBmYWNldF93cmFwKH4gZGF0YXNldCkgKw0KICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCBoanVzdCA9IDEpKSArDQogIGxhYnMoeCA9ICJBc2lzdGVuY2lhIGFsIGNpbmUsIHRlYXRybyB5L28gcmVjaXRhbGVzIGVuIGVsIMO6bHRpbW8gYcOxbyIsIHkgPSAiUHJvcG9yY2nDs24gZW4gJSIsIHRpdGxlID0gIlByb3BvcmNpw7NuIGRlIGFzaXN0ZW5jaWEgcG9yIGRhdGFzZXQiKSArIA0KICB0aGVtZV9idygpICsNCiAgc2NhbGVfZmlsbF9icmV3ZXIocGFsZXR0ZT0iU2V0MSIpDQoNCmBgYA0KDQpPYnNlcnZhbW9zIHF1ZSBwcsOhY3RpY2FtZW50ZSAqbm8gZXhpc3RlIGRlc2JhbGFuY2UgZW4gbGFzIGNsYXNlcyBkZSBsYSB2YXJpYWJsZSBhIHByZWRlY2lyKi4gRXN0byBlcyBpbXBvcnRhbnRlIHBvcnF1ZSBjbGFzZXMgZGVzYmFsYW5jZWFkYXMgcHVlZGVuIGFmZWN0YXIgbGFzIGVzdGltYWNpb25lcyBkZWwgbW9kZWxvIHkgc3UgY2xhc2lmaWNhY2nDs24gZmluYWwuICAgDQoNCg0KIyMjIENyZWFjacOzbiBkZSBtb2RlbG9zDQoNCkxhICoqUmVncmVzacOzbiBMb2fDrXN0aWNhIGVzIHVuYSBkZSBsYXMgYXBsaWNhY2lvbmVzIHBvc2libGVzIGRlIGxvcyBNb2RlbG9zIExpbmVhbGVzIEdlbmVyYWxpemFkb3MgKEdMTSkqKi4gDQpMYSBmdW5jw63Ds24gZ2xtKCksIGFsIGlndWFsIHF1ZSBsYSBmdW5jacOzbiBsbSgpLCB0b21hIGNvbW8gYXJndW1lbnRvcyB1bmEgZm9ybXVsYSB5IGxvcyBkYXRvcywgcGVybyBlc3RlIGNhc28gdGFtYmnDqW4gc2UgZGViZSBlc3BlY2lmaWNhciBlbCBhcmd1bWVudG8gZmFtaWx5OiBpbmRpY2Ftb3MgbGEgZGlzdHJpYnVjacOzbiBkZWwgZXJyb3IgeSBsYSBmdW5jacOzbiBsaW5rIHF1ZSB2YW1vcyBhIHV0aWxpemFyIGVuIGVsIG1vZGVsby4NCg0KQWxndW5hcyBmYW1pbGlhcyBzb246DQoNCi0gQmlub21pYWw6IGxpbms9bG9naXQNCg0KLSBQb2lzc29uOiBsaW5rPWxvZw0KDQotIEdhdXNzaWFuYTogbGluaz1pZGVudGlkYWQNCg0KQ29tbyBlc3RhbW9zIHRyYWJhamFuZG8gY29uIHVuIGZlbsOzbWVubyBxdWUgc3Vwb25lbW9zIHRpZW5lIHVuYSBkaXN0cmlidWNpw7NuIGJpbm9taWFsLCBhc8OtIGxvIGVzcGVjaWZpY2Ftb3MgZW4gZWwgcGFyw6FtZXRybyBmYW1pbHkuIEEgY29udGludWFjacOzbiwgcmVhbGl6YW1vcyB1bmEgc2VyaWUgZGUgbW9kZWxvcyBkZSByZWdyZXNpw7NuIGxvZ8Otc3RpY2EgcGFyYSBwcmVkZWNpciBsYSBhc2lzdGVuY2lhIG8gbm8gYWwgY2luZSwgdGVhdHJvIHkvbyByZWNpdGFibGVzIGVuIGVsIMO6bHRpbW8gYcOxbyBkZSBsYXMgcGVyc29uYXMgZW50cmV2aXN0YWRhcywgZW4gZnVuY2nDs24gbGFzIGNhcmFjdGVyw61zdGljYXMgc29jaW9kZW1vZ3LDoWZpY2FzIHNlbGVjY2lvbmFkYXMuDQoNClBhcmEgKipjcmVhciB2YXJpb3MgbW9kZWxvcyBkZSByZWdyZXNpw7NuIGxvZ8Otc3RpY2EqKiB1dGlsaXphbW9zIGxhIGZ1bmNpw7NuIGBmb3JtdWxhc2AgZGVsIHBhcXVldGUgKiptb2RlbHIqKiwgY3JlYW1vcyBhc8OtIHVuIG9iamV0byBxdWUgY29udGllbmUgdG9kYXMgbGFzIGbDs3JtdWxhcyBxdWUgdmFtb3MgYSB1dGlsaXphci4gRW4gYC5yZXNwb25zZWAgZXNwZWNpZmljYW1vcyBsYSB2YXJpYWJsZSByZXNwdWVzdGEgZGUgbnVlc3RyYXMgZsOzcm11bGFzIHkgbHVlZ28gbm9tYnJhbW9zIGxhcyBmw7NybXVsYXMgcXVlIHF1ZXJhbW9zIGFybWFyLg0KDQpMdWVnbyBkZSBjcmVhciBsYXMgZm9ybcO6bGFzLCBjcmVhbW9zIGxvcyBtb2RlbG9zIHkgbG9zIGFuYWxpemFtb3MgZGUgZm9ybWEgY29tcGFyYXRpdmEuDQoNCmBgYHtyfQ0KIyBDcmVhY2nDs24gZGUgZsOzcm11bGFzDQpsb2dpdF9mb3JtdWxhcyA8LSBmb3JtdWxhcygucmVzcG9uc2UgPSB+IGFzaXN0ZW5jaWEsDQogICAgICAgICAgICAgICAgICAgICAgICAgICBjb252aXZlID0gfiBjb252aXZlX25pw7F4c19hZHVsdG9zbWF5b3JlcywgDQogICAgICAgICAgICAgICAgICAgICAgICAgICByZWdpb24gPSB+IHJlZ2lvbiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgIHNleG8gPSB+IHNleG8sIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgZnJhbmphX2V0YXJpYSA9IH4gZnJhbmphX2V0YXJpYSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICBQU0UgPSB+IHAxMDcsDQogICAgICAgICAgICAgICAgICAgICAgICAgICBzZXhvX1BTRSA9IH4gc2V4byArIHAxMDcsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgc2V4b19jb252aXZlID0gfiBzZXhvICsgY29udml2ZV9uacOxeHNfYWR1bHRvc21heW9yZXMsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgY29udml2ZV9yZWdpb25fUFNFID0gfiBjb252aXZlX25pw7F4c19hZHVsdG9zbWF5b3JlcyArIHJlZ2lvbiArIHAxMDcsDQogICAgICAgICAgICAgICAgICAgICAgICAgICBmcmFuamFfcmVnaW9uX1BTRSA9IH4gZnJhbmphX2V0YXJpYSArIHJlZ2lvbiArIHAxMDcsDQogICAgICAgICAgICAgICAgICAgICAgICAgICApDQoNCmxvZ2l0X2Zvcm11bGFzICMgb2JzZXJ2YW1vcyBlbCBvYmpldG8gZm9ybXVsYXMNCmBgYA0KUHJvY2VkZW1vcyBhIGNyZWFyIGxvcyBtb2RlbG9zIGEgcGFydGlyIGRlIGVzdGFzIGbDs3JtdWxhcy4NCg0KYGBge3IsIHdhcm5pbmc9RkFMU0V9DQptb2RlbHMgPC0gZGF0YV9mcmFtZShsb2dpdF9mb3JtdWxhcykgJT4lICMgZGF0YWZyYW1lIGEgcGFydGlyIGRlbCBvYmpldG8gZm9ybXVsYXMNCiAgbXV0YXRlKG1vZGVscyA9IG5hbWVzKGxvZ2l0X2Zvcm11bGFzKSwgIyBjb2x1bW5hIGNvbiBsb3Mgbm9tYnJlcyBkZSBsYXMgZm9ybXVsYXMNCiAgICAgICAgIGV4cHJlc3Npb24gPSBwYXN0ZShsb2dpdF9mb3JtdWxhcyksICMgY29sdW1uYSBjb24gbGFzIGV4cHJlc2lvbmVzIGRlIGxhcyBmb3JtdWxhcw0KICAgICAgICAgbW9kID0gbWFwKGxvZ2l0X2Zvcm11bGFzLCB+Z2xtKC4sIGZhbWlseSA9ICdiaW5vbWlhbCcsIGRhdGEgPSB0cmFpbl9kYXRhKSkpIA0KbW9kZWxzDQpgYGANCg0KDQojIyMgSW50ZXJwcmV0YWNpw7NuOiBNb2RlbG9zIHNpbXBsZXMNCg0KQW5hbGl6YW1vcyBsb3MgcHJpbWVyb3MgY2luY28gbW9kZWxvcywgYXF1ZWxsb3MgcXVlIHRpZW5lbiB1biDDum5pY28gcHJlZGljdG9yLiBVc2Ftb3MgbGEgZnVuY2nDs24gX3RpZHlfIHBhcmEgb2J0ZW5lciBsb3MgcGFyw6FtZXRyb3MgZXN0aW1hZG9zIHBhcmEgZXN0b3MgY2luY28gbW9kZWxvcy4NCg0KDQpgYGB7ciwgd2FybmluZz1GQUxTRX0NCm1vZGVscyAlPiUgDQogIGZpbHRlcihtb2RlbHMgJWluJSBjKCdyZWdpb24nLCdzZXhvJywnZnJhbmphX2V0YXJpYScsJ2NvbnZpdmUnLCdQU0UnKSkgJT4lDQogIG11dGF0ZSh0aWR5ID0gbWFwKG1vZCwgdGlkeSkpICU+JQ0KICB1bm5lc3QodGlkeSkgJT4lIA0KICBtdXRhdGUoZXN0aW1hdGU9cm91bmQoZXN0aW1hdGUsNSksICMgcmVkb25kZWFtb3MgdmFsb3JlcyBwYXJhIGZhY2lsaXRhciBsZWN0dXJhDQogICAgICAgICBwLnZhbHVlPXJvdW5kKHAudmFsdWUsNCkpDQoNCmBgYA0KDQojIyMjIEludGVycHJldGFjacOzbiBkZSBsb3MgY29lZmljaWVudGVzOg0KDQoqKk1vZGVsbyBjb252aXZlKioNCg0KzrIwID0gMC4yMzEzNiBjb3JyZXNwb25kZSBhIGxhcyBwZXJzb25hcyBxdWUgbm8gY29udml2ZW4gY29uIG5pw7F4cywgYWRvbGVjZW50ZXMgeS9vIGFsZHVsdG9zIG1heW9yZXMuDQoNCs6yMSA9IC0wLjE4NTI1IHJlcHJlc2VudGEgYSBsYXMgcGVyc29uYXMgcXVlIGNvbnZpdmVuIGNvbiBuacOxeHMsIGFkb2xlY2VudGVzIHkvbyBhZHVsdG9zIG1heW9yZXMgeSBlbCBzaWdubyBuZWdhdGl2byBkZWwgY29lZmljaWVudGUgaW5kaWNhIHF1ZSBsYSBwcm9iYWJpbGlkYWQgZGUgYXNpc3RlbmNpYSBhbCBjaW5lLCB0ZWF0cm8geS9vIHJlY2l0YWxlcyBlbiBlbCDDumx0aW1vIGHDsW8gc2UgcmVkdWNlIGVuIGNvbXBhcmFjacOzbiBjb24gbG9zIHF1ZSBubyBjb252aXZlbi4gRWwgcC12YWxvcj4wLjA1IGluZGljYSBxdWUgc2UgdHJhdGEgZGUgdW5hIGRpZmVyZW5jaWEgZXN0YWTDrXN0aWNhbWVudGUgc2lnbmlmaWNhdGl2YS4NCg0KKipNb2RlbG8gcmVnacOzbioqDQoNCs6yMCA9IDAuOTM5ODIgY29ycmVzcG9uZGUgYSBsYXMgcGVyc29uYXMgZW50cmV2aXN0YWRhcyBxdWUgdml2ZW4gZW4gQ0FCQS4NCg0KzrIxIGEgzrI2ID0gTG9zIGNvZWZpY2llbnRlcyBlc3RpbWFkb3MgZW4gdG9kb3MgY2Fzb3Mgc29uIG5lZ2F0aXZvcywgaW5kaWNhbmRvIHF1ZSBsYSBwcm9iYWJpbGlkYWQgZGUgaGFiZXIgYXNpc3RpZG8gYWwgY2luZSwgdGVhdHJvIHkvbyByZWNpdGFsZXMgZW4gZWwgw7psdGltbyBhw7FvIGRlIGxhcyBwZXJzb25hcyBxdWUgdml2ZW4gZW4gbGFzIGRpc3RpbnRhcyByZWdpb25lcyBkZWwgcGHDrXMgZGlzbWludXllIGVuIGNvbXBhcmFjacOzbiBjb24gbGFzIHBlcnNvbmFzIHF1ZSB2aXZlbiBlbiBDQUJBLiAgT2JlcnZhbmRvIGxvcyB2YWxvcmVzIGRlIGxvcyBjb2VmaWNpZW50ZXMsIGxhcyBjYXRlZ29yw61hcyBzZSBwdWVkZW4gb3JkZW5hciBkZSBtZW5vciBhIG1heW9yLiBFbiBlc3RlIHNlbnRpZG8sIGVzIGVzcGVyYWJsZSBxdWUgbGEgcHJvYmFiaWxpZGFkIGRlIGhhYmVyIGFzaXN0aWRvIGFsIGNpbmUsIHRlYXRybyB5L28gcmVjaXRhbGVzIGVuIGVsIMO6bHRpbW8gYcOxbyBzZWEgYcO6biBtZW5vciBwYXJhIGVsIE5PQSAoLTEuNDMpIHF1ZSBwYXJhIGxhIHF1aWVuZXMgdml2ZW4gZW4gbGEgcmVnacOzbiBDRU5UUk8gKC0wLjM4KS4NCg0KRW4gdG9kb3MgbG9zIGNhc29zLCBsb3MgcC12YWxvcmVzIHNvbiBtZW5vcmVzIHF1ZSAwLjA1LCBsbyBjdWFsIGluZGljYSBxdWUgbGFzIGRpZmVyZW5jaWFzIGNvbiBDQUJBIChjYXRlZ29yw61hIGJhc2FsKSBzb24gZXN0YWTDrXN0aWNhbWVudGUgc2lnbmlmaWNhdGl2YXMuDQoNCioqTW9kZWxvIHNleG8qKg0KDQrOsjAgPSAwLjEzODg3IGNvcnJlc3BvbmRlIGEgbGFzIG11amVyZXMgZW50cmV2aXN0YWRhcy4NCg0KzrIxID0gLTAuMDYwNTIgcmVwcmVzZW50YSBhIGxvcyB2YXJvbmVzIGVudHJldmlzdGFkb3MgZSBpbmRpY2EgcXVlIGxhIHByb2JhYmlsaWRhZCBkZSBhc2lzdGVuY2lhIGFsIGNpbmUsIHRlYXRybyB5L28gcmVjaXRhbGVzIHNlIHJlZHVjZSBlbiBjb21wYXJhY2nDs24gYSBsYXMgbXVqZXJlcy4gU2luIGVtYmFyZ28sIGVsIHAtdmFsb3I+MC4wNSBpbmRpY2EgcXVlIG5vIHNlIHRyYXRhIGRlIHVuYSBkaWZlcmVuY2lhIGVzdGFkw61zdGljYW1lbnRlIHNpZ25pZmljYXRpdmEuIA0KDQoNCioqTW9kZWxvIGZyYW5qYV9ldGFyaWEqKg0KDQrOsjAgPSAwLjgzNzA0IGNvcnJlc3BvbmRlIGEgbGFzIHBlcnNvbmFzIGVudHJldmlzdGFkYXMgcXVlIHRpZW5lbiBlbnRyZSAxMiB5IDE3IGHDsW9zLg0KDQrOsjEgYSDOsjQgPSBMb3MgY29lZmljaWVudGVzIGVzdGltYWRvcyBlbiB0b2RvcyBjYXNvcyBzb24gbmVnYXRpdm9zLCBpbmRpY2FuZG8gcXVlIGxhIHByb2JhYmlsaWRhZCBkZSBoYWJlciBhc2lzdGlkbyBhbCBjaW5lLCBhbCB0ZWF0cm8geS9vIGEgcmVjaXRhbGVzIGVuIGVsIMO6bHRpbW8gYcOxbyBkZSBsYXMgcGVyc29uYXMgZGUgMTggYcOxb3MgbyBtw6FzIGRpc21pbnV5ZSBlbiBjb21wYXJhY2nDs24gY29uIGxvcyBtw6FzIGrDs3ZlbmVzLiAgT2JlcnZhbmRvIGxvcyB2YWxvcmVzIGRlIGxvcyBjb2VmaWNpZW50ZXMsIGxhcyBjYXRlZ29yw61hcyBzZSBwdWVkZSBvcmRlbmFyIGRlIG1lbm9yIGEgbWF5b3IuIEVuIGVzdGUgc2VudGlkbywgZXMgZXNwZXJhYmxlIHF1ZSBsYSBwcm9iYWJpbGlkYWQgZGUgaGFiZXIgYXNpc3RpZG8gYWwgY2luZSBlbiBlbCDDumx0aW1vIGHDsW8gc2VhIGHDum4gbWVub3IgcGFyYSBsYXMgcGVyc29uYXMgZGUgNjUgYcOxb3MgbyBtw6FzICgtMS45MCkgcXVlIHBhcmEgbGEgcXVpZW5lcyB0aWVuZW4gZW50cmUgMTggeSAyOSBhw7FvcyAoLTAuMTgpLg0KQWwgbWlyYXIgbG9zIHAtdmFsb3Jlcywgc2Ugb2JzZXJ2YSBxdWUgc8OzbG8gcGFyYSBsYSBmcmFuamEgZXRhcmlhIGRlIDE4IGEgMjksIGxhIGRpZmVyZW5jaWEgY29uIGxhIGNhdGVnb3LDrWEgYmFzYWwgKHBlcnNvbmFzIGRlIDEyIGEgMTcpIG5vIGVzIGVzdGFkw610aWNhbWVudGUgc2lnbmlmaWNhdGl2YSAocC12YWxvcj4wLjA1KS4gDQoNCg0KKipNb2RlbG8gUFNFIChQcmluY2lwYWwgU29zdMOpbiBFY29uw7NtaWNvIGRlbCBIb2dhcikqKg0KDQrOsjAgPSAtMC4xNTQxNSBjb3JyZXNwb25kZSBhIGxhcyBwZXJzb25hcyBlbnRyZXZpc3RhZGFzIHF1ZSBzb24gZWwgcHJpbmNpcGFsIHNvc3TDqW4gZWNvbsOzbWljbyBkZSBob2dhciAoUFNFKS4NCg0KzrIxID0gMC41NTIxOCByZXByZXNlbnRhIGEgcXVpZW5lcyBubyBzb24gZWwgUFNFLiBFbCBzaWdubyBwb3NpdGl2byBkZWwgY29lZmljaWVudGUgaW5kaWNhIHF1ZSBsYSBwcm9iYWJpbGlkYWQgZGUgYXNpc3RlbmNpYSBhbCBjaW5lLCBhbCB0ZWF0cm8geS9vIGEgcmVjaXRhbGVzIGVuIGVsIMO6bHRpbW8gYcOxbyBlcyBtYXlvciBwYXJhIHF1aWVuZXMgbm8gc29uIGplZnhzIGRlIGhvZ2FyZXMgcXVlIHBhcmEgcXVpZW5lcyBzw60gbG8gc29uLiBFbCBwLXZhbG9yPDAuMDUgaW5kaWNhIHF1ZSBzZSB0cmF0YSBkZSB1bmEgZGlmZXJlbmNpYSBlc3RhZMOtc3RpY2FtZW50ZSBzaWdpbmZpY2F0aXZhLiANCg0KDQoNCiMjIyBJbnRlcnByZXRhY2nDs246IE1vZGVsb3MgbcO6bHRpcGxlcw0KDQpBIGNvbnRpbnVhY2nDs24gYW5hbGl6YW1vcyBsb3MgY3VhdHJvIG1vZGVsb3MgbcO6bHRpcGxlcy4NCg0KYGBge3IsIHdhcm5pbmc9RkFMU0V9DQptb2RlbHMgJT4lIA0KICBmaWx0ZXIobW9kZWxzICVpbiUgYygnc2V4b19QU0UnLCdzZXhvX2NvbnZpdmUnLCdjb252aXZlX3JlZ2lvbl9QU0UnLCAnZnJhbmphX3JlZ2lvbl9QU0UnKSkgJT4lDQogIG11dGF0ZSh0aWR5ID0gbWFwKG1vZCwgdGlkeSkpICU+JQ0KICB1bm5lc3QodGlkeSkgJT4lIA0KICBtdXRhdGUoZXN0aW1hdGU9cm91bmQoZXN0aW1hdGUsNSksICMgcmVkb25kZWFtb3MgdmFsb3JlcyBwYXJhIGZhY2lsaXRhciBsZWN0dXJhDQogICAgICAgICBwLnZhbHVlPXJvdW5kKHAudmFsdWUsNCkpDQoNCmBgYA0KIyMjIyBJbnRlcnByZXRhY2nDs24gZGUgbG9zIGNvZWZpY2llbnRlczoNCg0KKipNb2RlbG8gc2V4b19QU0UqKg0KDQrOsjAgPSAtMC4yMTIwMy4gQWwgdHJhdGFyc2UgZGUgdW4gbW9kZWxvIGNvbiBkb3MgdmFyaWFibGVzIGV4cGxpY2F0aXZhcyBjYXRlZ8OzcmljYXMsIGVsIEludGVyY2VwdCByZXByZXNlbnRhIGVuIGVzdGUgY2FzbyBhIGxhcyBtdWplcmVzIHF1ZSBzb24gZWwgcHJpbmNpcGFsIHNvc3TDqW4gZWNvbsOzbWljbyBkZSBzdXMgaG9nYXJlcy4NCg0KzrIxID0gMC4wOTI0MS4gQ29ycmVzcG9uZGUgYSBsYSBjYXRlZ29yw61hICp2YXLDs24qIGRlIGxhIHZhcmlhYmxlICpzZXhvKi4gU2Ugb2JzZXJ2YSBxdWUgZWwgY29lZmljaWVudGUgZXMgcG9zaXRpdm8uIEVzdG8gc2lnbmlmaWNhcsOtYSBxdWUgbGEgcHJvYmFiaWxpZGFkIGRlIGFzaXN0ZW5jaWEgYWwgY2luZSwgYWwgdGVhdHJvIHkvbyBhIHJlY2l0YWxlcyBlcyBtYXlvciByZXNwZWN0byBhIGxhIGRlIGxhcyBtdWplcmVzLCBkYWRhcyBsYXMgZGVtw6FzIHZhcmlhYmxlcyBkZWwgbW9kZWxvLiBTaW4gZW1iYXJnbywgZWwgcC12YWxvcj4wLjA1IGluZGljYSBxdWUgbm8gc2UgdHJhdGEgZGUgdW5hIGRpZmVyZW5jaWEgZXN0YWTDrXN0aWNhbWVudGUgc2lnbmlmaWNhdGl2YS4gDQoNCs6yMiA9IDAuNTc2ODkuIENvcnJlc3BvbmRlIGEgbGEgY2F0ZWdvcsOtYSAqb3RybyBtaWVtYnJvIGRlbCBob2dhciogZGUgbGEgdmFyaWFibGUgKlBTRSouIFNlIG9ic2VydmEgcXVlIGVsIGNvZWZpY2llbnRlIGVzIHBvc2l0aXZvLiBFc3RvIHNpZ25pZmljYSBxdWUgbGEgcHJvYmFiaWxpZGFkIGRlIGFzaXN0ZW5jaWEgZXMgbWF5b3IgcmVzcGVjdG8gYSBsYSBkZSBsYXMgcGVyc29uYXMgcXVlIHNvbiBlbCBwcmluY2lwYWwgc29zdMOpbiBlY29uw7NtaWNvIGRlbCBob2dhciBkb25kZSB2aXZlbiwgZGFkYXMgbGFzIGRlbcOhcyB2YXJpYWJsZXMgZGVsIG1vZGVsby4gRWwgcC12YWxvcjwwLjA1IGluZGljYSBxdWUgc2UgdHJhdGEgZGUgdW5hIGRpZmVyZW5jaWEgZXN0YWTDrXN0aWNhbWVudGUgc2lnbmlmaWNhdGl2YS4gDQoNCioqTW9kZWxvIHNleG9fY29udml2ZSoqDQoNCs6yMCA9IDAuMjY3NzIuIEVuIGVzdGUgY2FzbywgZWwgSW50ZXJjZXB0IHJlcHJlc2VudGEgYSBsYXMgKm11amVyZXMqIHF1ZSAqbm8gY29udml2ZW4gY29uIG5pw7F4cywgYWRvbGVjZW50ZXMgeS9vIGFkdWx0b3MgbWF5b3JlcyouDQoNCs6yMSA9IC0wLjA2ODU0IHkgzrIyID0gLTAuMTg4NTEuIEFtYm9zIGNvZWZpY2llbnRlcyBzb24gbmVnYXRpdm9zLCBwb3IgbG8gcXVlIHJlcHJlc2VudGFyw61hbiB1bmEgcHJvYmFibGlsaWRhZCBtZW5vciBkZSBhc2lzdGVuY2lhIGFsIGNpbmUgZW4gZWwgw7psdGltbyBhw7FvIHBhcmEgKmFtYmFzIGNhdGVnb3LDrWFzKiwgZGFkYXMgbGFzIGRlbcOhcyB2YXJpYWJsZXMgZGVsIG1vZGVsby4gU2luIGVtYmFyZ28sIGFsIG1pcmFyIGxvcyBwLXZhbG9yZXMsIHNlIG9ic2VydmEgcXVlIHPDs2xvIGVsIGhlY2hvIGRlIGNvbnZpdmlyIG8gbm8gY29uIG5pw7F4cywgYWRvbGVjZW50ZXMgeS9vIGFkdWx0b3MgbWF5b3JlcyByZXByZXNlbnRhIHVuYSBkaWZlcmVuY2lhIGVzdGFkw61zdGljYW1lbnRlIHNpZ25pZmljYXRpdmEgKHAtdmFsb3I9MC4wNDIwKS4gDQoNCioqTW9kZWxvIGNvbnZpdmVfcmVnaW9uX1BTRSoqDQoNCs6yMCA9IDAuNzk0MTEuIEVuIGVzdGUgY2FzbywgZWwgSW50ZXJjZXB0IHJlcHJlc2VudGEgYSBsYXMgcGVyc29uYXMgcXVlIG5vIGNvbnZpdmVuIGNvbiBuacOxeHMsIGFkb2xlY2VudGVzIG5pIGFkdWx0b3MgbWF5b3JlcywgcXVlIHZpdmVuIGVuIENBQkEgeSBzb24gZWwgcHJpbmNpcGFsIHNvc3TDqW4gZWNvbsOzbWljbyBkZWwgaG9nYXIgZG9uZGUgdml2ZW4uDQoNCs6yMSA9IC0wLjIyMTgxLiBDb3JyZXNwb25kZSBhIGxhcyBwZXJzb25hcyBxdWUgY29udml2ZW4gY29uIG5pw7F4cywgYWRvbGVjZW50ZXMgeS9vIGFkdWx0b3MgbWF5b3Jlcy4gRWwgc2lnbm8gbmVnYXRpdm8gZGVsIGNvZWZpY2llbnRlIGluZGljYSBxdWUgbGEgcHJvYmFibGlkYWQgZGUgaGFiZXIgYXNpc3RpZG8gYWwgY2luZSwgYWwgdGVhdHJvIHkvbyBhIHJlY2l0YWxlcyBlbiBlbCDDumx0aW1vIGHDsW8gZGlzbWludXllLCBlbiBjb21wYXJhY2nDs24gY29uIGxhcyBwZXJzb25hcyBxdWUgbm8gY29udml2ZW4gY29uIG5pw7F4cywgYWRvbGVjZW50ZXMgbmkgYWR1bHRvcyBtYXlvcmVzLiBFbCBwLXZhbG9yIDwgMC4wNSwgaW5kaWNhIHF1ZSBzZSB0cmF0YSBkZSB1bmEgZGlmZXJlbmNpYSBlc3RhZMOtc3RpY2FtZW50ZSBzaWduaWZpY2F0aXZhLiANCg0KDQrOsjIgYSDOsjcuIENvcnJlc3BvbmRlbiBhIGxhcyBwZXJzb25hcyBxdWUgbm8gdml2ZW4gZW4gQ0FCQS4gRW4gbMOtbmVhIGNvbiBsbyBxdWUgc2Ugb2JlcnbDsyBlbiBlbCBtb2RlbG8gc2ltcGxlLCB0b2RvcyBsb3MgY29lZmljaWVudGVzIHNvbiBuZWdhdGl2b3MsIHBvciBsbyBxdWUgZW4gbGFzIHNlaXMgcmVnaW9uZXMgZGVsIHBhw61zIGFuYWxpemFkYXMgbGEgcHJvYmFibGlkYWQgZGUgaGFiZXIgYXNpc3RpZG8gYWwgY2luZSwgYWwgdGVhdHJvIHkvbyBhIHJlY2l0YWxlcyBlbiBlbCDDumx0aW1vIGHDsW8gZGlzbWludXllIGVuIHJlbGFjacOzbiBhIHF1aWVuZXMgdml2ZW4gZW4gQ0FCQS4gTG9zIHAtdmFsb3JlczwwLjA1IGluZGljYW4gcXVlIGxhcyBkaWZlcmVuY2lhcyBzb24gZXN0YWTDrWN0aWNhbWVudGUgc2lnbmlmaWNhdGl2YXMgZW4gdG9kb3MgbG9zIGNhc29zLiANCg0KDQrOsjg9IDAuNjY2NjMuIENvcnJlc3BvZGUgYSBsYXMgcGVyc29uYXMgcXVlIG5vIHNvbiBlbCBwcmluY2lwYWwgc29zdMOpbiBlY29uw7NtaWNvIGRlbCBob2dhci4gRWwgY29lZmljaWVudGUgcG9zaXRpdm8gc2lnbmlmaWNhIHF1ZSBsYXMgcGVyc29uYXMgcXVlIG5vIHNvbiBlbCBQU0UsIHRpZW5lbiB1bmEgcHJvYmFibGlkYWQgbWF5b3IgZGUgaGFiZXIgYXNpc3RpZG8gYWwgY2luZSwgYWwgdGVhdHJvIHkvbyBhIHJlY2l0YWxlcyBlbCDDumx0aW1vIGHDsW8sIGVuIGNvbXBhcmFjacOzbiBjb24gbGFzIHF1ZSBzw60gbG8gc29uLCBkYWRhcyBlbCByZXN0byBkZSBsYXMgdmFyaWFibGVzIGRlbCBtb2RlbG8uIEVsIHAtdmFsb3I8MC4wNSBpbmRpY2EgcXVlIHNlIHRhcnRhIGRlIHVuYSBkaWZlcmVuY2lhIGVzdGFkw61jdGljYW1lbnRlIHNpZ25pZmljYXRpdmEuIA0KDQoNCioqTW9kZWxvIGZyYW5qYV9yZWdpb25fUFNFKioNCg0KzrIwID0gMS44NjkwNy4gRW4gZXN0ZSBjYXNvLCBlbiBlbCBJbnRlcmNlcHQgcmVwcmVzZW50YSBhIGxhcyBwZXJzb25hcyBlbnRyZSAxMiB5IDE3IGHDsW9zLCBxdWUgdml2ZW4gZW4gQ0FCQSB5IHNvbiBlbCBwcmluY2lwYWwgc29zdMOpbiBlY29uw7NtaWNvIGRlbCBob2dhciBkb25kZSB2aXZlbi4NCg0KzrIxIGEgzrI0LiBDb3JyZXNwb25kZSBhIGxhcyBwZXJzb25hcyBkZSAxOCBhw7FvcyBvIG3DoXMsIGRpdmlkaWRhcyBwb3IgZnJhbmphcyBldGFyaWFzLiBFbiBsw61uZWEgY29uIGxvIHF1ZSBzZSBvYmVydsOzIGVuIGVsIG1vZGVsbyBzaW1wbGUsIHRvZG9zIGxvcyBjb2VmaWNpZW50ZXMgc29uIG5lZ2F0aXZvcywgcG9yIGxvIHF1ZSBwYXJhIGxhcyBwZXJzb25hcyBtYXlvcmVzIGRlIDE4IGHDsW9zIGxhIHByb2JhYmxpZGFkIGRlIGhhYmVyIGFzaXN0aWRvIGRpc21pbnV5ZSBlbiByZWxhY2nDs24gYSBxdWllbmVzIHNvbiBtZW5vcmVzIGRlIGVkYWQuIExvcyBwLXZhbG9yZXM8MC4wNSAoZW4gdG9kb3MgbG9zIGNhc29zLCBtZW5vcyBwYXJhIGxhcyBwZXJzb25hcyBxdWUgdGllbmVuIGVudHJlIDE4IHkgMjkgYcOxb3MpIGluZGljYW4gcXVlIGxhcyBkaWZlcmVuY2lhcyBzb24gZXN0YWTDrWN0aWNhbWVudGUgc2lnbmlmaWNhdGl2YXMuDQoNCg0KzrI1IGEgzrIxMC4gQ29ycmVzcG9uZGVuIGEgbGFzIHBlcnNvbmFzIHF1ZSBubyB2aXZlbiBlbiBDQUJBLiBDb21vIGVuIGVsIG1vZGVsbyBhbnRlcmlvciwgdG9kb3MgbG9zIGNvZWZpY2llbnRlcyBzb24gbmVnYXRpdm9zLCBwb3IgbG8gcXVlIGVuIGxhcyBzZWlzIHJlZ2lvbmVzIGRlbCBwYcOtcyBhbmFsaXphZGFzIGxhIHByb2JhYmxpZGFkIGRlIGhhYmVyIGFzaXN0aWRvIGFsIGNpbmUsIGFsIHRlYXRybyB5L28gYSByZWNpdGFsZXMgZW4gZWwgw7psdGltbyBhw7FvIGRpc21pbnV5ZSBlbiByZWxhY2nDs24gYSBxdWllbmVzIHZpdmVuIGVuIENBQkEsIGRhZGFzIGVsIHJlc3RvIGRlIGxhcyB2YXJpYWJsZXMgZGVsIG1vZGVsby4gTG9zIHAtdmFsb3JlczwwLjA1IGluZGljYW4gcXVlIGxhcyBkaWZlcmVuY2lhcyBzb24gZXN0YWTDrWN0aWNhbWVudGUgc2lnbmlmaWNhdGl2YXMgZW4gdG9kb3MgbG9zIGNhc29zLiANCg0KzrIxMT0gMC4wNDkyMC4gQ29ycmVwb25kZSBhIGxhcyBwZXJzb25hcyBxdWUgbm8gc29uIGVsIHByaW5jaXBhbCBzb3N0w6luIGVjb27Ds21pY28gZGVsIGhvZ2FyIGVuIGVsIHF1ZSB2aXZlbi4gRWwgY29lZmljaWVudGUgZGUgc2lnbm8gcG9zaXRpdm8gaW5kaWNhcsOtYSBxdWUgbGFzIHBlcnNvbmFzIHF1ZSBubyBlc3TDoW4gYSBjYXJnbyBlY29uw7NtaWNhbWVudGUgZGUgc3UgaG9nYXIgdGllbmVuIG1heW9yIHByb2JhYmlsaWRhZCBkZSBoYWJlciBpZG8gYWwgY2luZSwgYWwgdGVhdHJvIHkvbyBhIHJlY2l0YWxlcyBlbiBlbCDDumx0aW1vIGHDsW8gcXVlIHF1aWVuZXMgc8OtIGxvIGVzdMOhbiwgZGFkYXMgZWwgcmVzdG8gZGUgbGFzIHZhcmlhYmxlcyBkZWwgbW9kZWxvLiBTaW4gZW1hYnJnbywgZWwgcC12YWxvcj4wLjA1IGluZGljYSBxdWUgbm8gc2UgdHJhdGEgZGUgdW5hIGRpZmVyZW5jaWEgZXN0YWTDrXN0aWNhbWVudGUgc2lnbmlmaWNhdGl2YS4gDQoNCg0KIyMgRXZhbHVhY2nDs24gZGUgdG9kb3MgbG9zIG1vZGVsb3MNCg0KRWwgcGFzbyBzaWd1aWVudGUgZXMgZXZhbHVhciBsb3MgbW9kZWxvcyBkZSBmb3JtYSBjb21wYXJhdGl2YSBwYXJhIGFuYWxpemFyIGN1w6FsIGRlIGVsbG9zIHNvbiBsb3MgcXVlIG1pbmltaXphbiBsYSAqKmRldmlhbmNlKiogKGNvbW8gZXhwcmVzacOzbiBkZSBsb3MgcmVzaWR1b3MpLiAgDQoNCkNvbiBtYXAoKSBhZ3JlZ2Ftb3MgbGEgZnVuY2nDs24gZ2xhbmNlIHBhcmEgdHJhZXIgaW5mb3JtYWNpw7NuIHJlbGV2YW50ZSBwYXJhIGxhIGV2YWx1YWNpw7NuIGRlbCBtb2RlbG8uIENvbiB1bm5lc3QoKSBhY2NlZGVtb3MgYSBkaWNoYSBpbmZvcm1hY2nDs24uIFBvciDDumx0aW1vLCBhZ3JlZ2Ftb3MgdW5hIGNvbHVtbmEgY29uIGVsIHBvcmNlbnRhamUgZGUgZGV2aWFuY2UgZXhwbGljYWRvIHBvciBjYWRhIG1vZGVsbyB5IG9yZGVuYW1vcyBlbCBkYXRhc2V0IHNlZ8O6biBzdSB2YWxvciBkZSBkZXZpYW5jZS4NCg0KYGBge3IsIHdhcm5pbmc9RkFMU0V9DQojIENhbGN1bGFyIGxhcyBtZWRpZGFzIGRlIGV2YWx1YWNpw7NuIHBhcmEgY2FkYSBtb2RlbG8NCm1vZGVscyA8LSBtb2RlbHMgJT4lIA0KICBtdXRhdGUoZ2xhbmNlID0gbWFwKG1vZCxnbGFuY2UpKQ0KDQojIE9idGVuZXIgbGFzIG1lZGlkYXMgZGUgZXZhbHVhY2lvbiBkZSBpbnRlcmVzDQptb2RlbHMgJT4lIA0KICB1bm5lc3QoZ2xhbmNlKSAlPiUNCiAgDQogICMgQ2FsY3VsYW1vcyBsYSBkZXZpYW5jZSBleHBsaWNhZGENCiAgbXV0YXRlKHBlcmNfZXhwbGFpbmVkX2RldiA9IDEtZGV2aWFuY2UvbnVsbC5kZXZpYW5jZSkgJT4lIA0KICBzZWxlY3QoLWMobW9kZWxzLCBkZi5udWxsLCBBSUMsIEJJQykpICU+JSANCiAgYXJyYW5nZShkZXZpYW5jZSkNCmBgYA0KDQpTZSBvYnNlcnZhIHF1ZSBlbCBtb2RlbG8gcXVlIG3DoXMgcmVkdWNlIGxhIGRldmlhbmNlIGRlbCBtb2RlbG8gKGNvbW8gZXhwcmVzacOzbiBkZSBsb3MgcmVzaWR1b3MpIGVzIGVsIHF1ZSBpbmNsdXllIGNvbW8gdmFyaWFibGVzIGV4cGxpY2F0aXZhcyBsYSBmcmFuamEgZXRhcmlhIGEgbGEgcXVlIHBlcnRlbmVjZSBsYSBwZXJzb25hIGVudHJldmlzdGFkYSwgbGEgcmVnacOzbiBkZWwgcGHDrXMgZW4gbGEgcXVlIHZpdmUgeSBzaSBlcyBvIG5vIGVsIHByaW5jaXBhbCBzb3N0w6luIGVjb27Ds21pY28gZGUgc3UgaG9nYXIgKFBTRSkuIEEgcGFydGlyIGRlbCBjw6FjbHVsbyBkZSAqZGV2aWFuY2UgZXhwbGljYWRhKiwgb2JzZXJ2YW1vcyBxdWUgZGljaG8gbW9kZWxvIGV4cGxpY2EgYXByb3hpbWFkYW1lbnRlIGVsIDEyJSBkZSBsYSB2YXJpYWJpbGlkYWQgZGVsIGZlbsOzbWVubyBxdWUgYnVzY2Ftb3MgY2FwdGFyLiANCg0KDQojIyMgR3LDoWZpY29zIGRlIEV2YWx1YWNpw7NuDQoNCkEgY29udGludWFjacOzbiwgZ3JhZmljYW1vcyBsYXMgbcOpdHJpY2FzIGRlbCBtZWpvciBtb2RlbG8gaGFsbGFkbyAoKiphc2lzdGVuY2lhfiBmcmFuamFfZXRhcmlhICsgcmVnaW9uICsgcDEwNyoqKSB2cy4gZWwgZGUgbWF5b3IgZGV2aWFuY2UgKCoqYXNpc3RlbmNpYX4gc2V4byoqKQ0KDQpDb21lbnphbW9zIGFncmVnYW5kbyBsYXMgcHJlZGljY2lvbmVzIGNvbiBgYXVnbWVudGAgY29uIGVsIHBhcsOhbWV0cm8gYHR5cGU9InJlc3BvbnNlImAuIExhIGZ1bmNpw7NuIGF1Z21lbnQgaGVyZWRhIGVsIGFyZ3VtZW50byB0eXBlLnByZWRpY3QgZGUgbGEgZnVuY2nDs24gcHJlZGljdC4NCg0KICAqIFNpIGB0eXBlLnByZWRpY3QgPSAnbGluaydgIGxhIHByZWRpY2Npw7NuIGVzIGVuIHTDqXJtaW5vcyBkZSBsYSBmdW5jacOzbiBsaW5rLiBFbiBudWVzdHJvIGNhc28gc29uIGVsIGxvZ2FyaXRtbyBkZSBsYXMgb2RkcywgZXMgZGVjaXIsIGxvcyB2YWxvcmVzIHF1ZSB0b21hIGxhIGV4cHJlc2nDs24gbG9naXQuDQogIA0KICAqIFNpIGB0eXBlLnByZWRpY3QgPSAncmVzcG9uc2UnYCBsYSBwcmVkaWNjacOzbiBzb24gbGFzIHByb2JhYmlsaWRhZGVzIGRlIHF1ZSBsYSBvYnNlcnZhY2nDs24gcGVydGVuZXpjYSBhIGxhIGNsYXNlIHBvc2l0aXZhLiBFbiBudWVzdHJvIGNhc28sIGRldnVlbHZlIGxhIHByb2JhYmlsaWRhZCBkZSBsYSBxdWUgcGVyc29uYSBoYXlhIGFzaXN0aWRvIGFsIGNpbmUsIGFsIHRlYXRybyB5L28gYSByZWNpdGFsZXMgZHVyYW50ZSBlbCDDumx0aW1vIGHDsW8uDQoNCmBgYHtyLCB3YXJuaW5nPUZBTFNFfQ0KIyBBw7FhZGlyIGxhcyBwcmVkaWNjaW9uZXMNCm1vZGVscyA8LSBtb2RlbHMgJT4lIA0KICBtdXRhdGUocHJlZD0gbWFwKG1vZCwgYXVnbWVudCwgdHlwZS5wcmVkaWN0ID0gInJlc3BvbnNlIikpDQoNCiNPYnNlcnZhY2lvbmVzIGNvbiBwcm9iYWJpbGlkYWQgbcOhcyBiYWphIChNRUpPUiBNT0RFTE8pDQptb2RlbHMkcHJlZCRmcmFuamFfcmVnaW9uX1BTRSAlPiUgYXJyYW5nZSguZml0dGVkKSAlPiUgaGVhZCgxMCkNCiNPYnNlcnZhY2lvbmVzIGNvbiBwcm9iYWJpbGlkYWQgbcOhcyBhbHRhIChNRUpPUiBNT0RFTE8pDQptb2RlbHMkcHJlZCRmcmFuamFfcmVnaW9uX1BTRSAlPiUgYXJyYW5nZShkZXNjKC5maXR0ZWQpKSAlPiUgaGVhZCgxMCkNCmBgYA0KDQpHdWFyZGFtb3MgbGFzIHByZWRpY2Npb25lcyBwYXJhIGxvcyBtb2RlbG9zIG1lbmNpb25hZG9zLg0KDQpgYGB7cn0NCiMgTW9kZWxvIGJ1ZW5vDQpwcmVkaWN0aW9uX2Z1bGwgPC0gbW9kZWxzICU+JSANCiAgZmlsdGVyKG1vZGVscz09ImZyYW5qYV9yZWdpb25fUFNFIikgJT4lIA0KICB1bm5lc3QocHJlZCkNCiNNb2RlbG8gbWFsbw0KcHJlZGljdGlvbl9iYWQgPC0gbW9kZWxzICU+JSANCiAgZmlsdGVyKG1vZGVscz09InNleG8iKSAlPiUgDQogIHVubmVzdChwcmVkKQ0KYGBgDQoNCjEpIEdyYWZpY2Ftb3MgKip2aW9saW4gcGxvdHMqKi4NCg0KYGBge3J9DQojIGdyYWZpY2Ftb3MgZWwgbW9kZWxvIGNvbXBsZXRvDQp2aW9saW5fZnVsbCA9IGdncGxvdChwcmVkaWN0aW9uX2Z1bGwsIGFlcyh4PWFzaXN0ZW5jaWEsIHk9LmZpdHRlZCwgZ3JvdXA9YXNpc3RlbmNpYSwgZmlsbD1mYWN0b3IoYXNpc3RlbmNpYSkpKSArIA0KICBnZW9tX3Zpb2xpbigpICsNCiAgdGhlbWVfYncoKSArDQogIGd1aWRlcyhzY2FsZT0ibm9uZSIpICsNCiAgbGFicyh0aXRsZT0nVmlvbGluIHBsb3QnLCBzdWJ0aXRsZT0nTW9kZWxvIGJ1ZW5vJywgeT0nUHJlZGljdGVkIHByb2JhYmlsaXR5JykNCiMgZ3JhZmljYW1vcyBlbCBtb2RlbG8gbWFsbw0KdmlvbGluX2JhZCA9IGdncGxvdChwcmVkaWN0aW9uX2JhZCwgYWVzKHg9YXNpc3RlbmNpYSwgeT0uZml0dGVkLCBncm91cD1hc2lzdGVuY2lhLCBmaWxsPWZhY3Rvcihhc2lzdGVuY2lhKSkpICsgDQogIGdlb21fdmlvbGluKCkgKyANCiAgdGhlbWVfYncoKSArDQogIGd1aWRlcyhzY2FsZT0ibm9uZSIpICsNCiAgbGFicyh0aXRsZT0nVmlvbGluIHBsb3QnLCBzdWJ0aXRsZT0nTW9kZWxvIG1hbG8nLCB5PSdQcmVkaWN0ZWQgcHJvYmFiaWxpdHknKQ0KIyBtb3N0cmFtb3MgYW1ib3MNCnBsb3RfZ3JpZCh2aW9saW5fYmFkLCB2aW9saW5fZnVsbCkNCmBgYA0KDQpFbiBsb3MgZ3LDoWZpY29zIGRlIHZpb2xpbiBvYnNlcnZhbW9zOiBFbiBlbCBlamUgZGUgYWJzY2lzYXMgbGEgY2xhc2UgdmVyZGFkZXJhIChhc2lzdGnDsyBvIG5vIGFzaXN0acOzIGVuIGVsIMO6bHRpbW8gYcOxbyBhbCB0ZWF0cm8sIGFsIGNpbm8geS9vIGEgdW4gcmVjaXRhbCkuIEVuIGVsIGVqZSBkZSBvcmRlbmFkYXMgbGEgcHJvYmFiaWxpZGFkIHByZWRpY2hhIHBvciBudWVzdHJvIG1vZGVsby4gRWwgZ3LDoWZpY28gbm9zIG11ZXN0cmEgbGEgZGlzdHJpYnVjacOzbiBkZSBsYSBjYW50aWRhZCBkZSBvYnNlcnZhY2lvbmVzIHBvciBzdSBjbGFzZSByZWFsIHkgbGEgcHJvYmFiaWxpZGFkIHF1ZSBsZSBhc2lnbmEgbnVlc3RybyBtb2RlbG8uDQoNCkxvIHF1ZSBvYnNlcnZhbW9zIGVuIGVsIG1vZGVsbyAiZnJhbmphX3JlZ2lvbl9QU0UiIChtb2RlbG8gYnVlbm8pIGVzIHF1ZSBwYXJhIGVsIHZhbG9yIHJlYWwgMSAoYXNpc3Rpw7MpLCBsYSBtYXlvciBwYXJ0ZSBkZSBsYXMgb2JzZXJ2YWNpb25lcyBzZSBjb25jZW50cmFuIGVuIGxhIG1pdGFkIHN1cGVyaW9yIGRlbCBncsOhZmljbywgcXVlIGNvcnJlc3BvbmRlIGEgbGFzIHByb2JhYmlsaWRhZCBwcmVkaWNoYXMgbcOhcyBhbHRhcyBkZSBwZXJ0ZW5lY2VyIGEgbGEgY2xhc2UgMS4gTG8gaW52ZXJzbywgYXVucXVlIGNvbiBtZW5vciBjbGFyaWRhZCwgc2Ugb2JzZXJ2YSBwYXJhIGVsIHZhbG9yIDAgKG5vIGFzaXN0acOzKS4NCg0KDQoyKSBSZWFsaXphbW9zIGEgY29udGludWFjacOzbiBlbCAqKmdyw6FmaWNvIGRlIEhvc21lci1MZW1lc2hvdyoqLg0KDQpTZSBnZW5lcmEgdW5hIGZ1bmNpw7NuIHBhcmEgcmVhbGl6YXIgdW4gZ3LDoWZpY28gZGUgSG9zbWVyLUxlbWVzaG93IHBhcmEgdW4gZGF0YXNldC4gUGFyYSBlbGxvIHNlIGZpamFuIGxvcyBzaWd1aWVudGVzIHBhcsOhbWV0cm9zOg0KDQotIGRhdGFzZXQ6IGNvbmp1bnRvIGRlIGRhdG9zDQoNCi0gcHJlZGljdGVkX2NvbHVtbjogY29sdW1uYSBjb24gbGEgcHJvYmFiaWxpZGFkIHByZWRpY2hhDQoNCi0gY2xhc3NfY29sdW1uOiBjb2x1bW5hIGNvbiBsYSBjbGFzZSBhIHByZWRlY2lyDQoNCi0gcG9zc2l0aXZlX3ZhbHVlOiB2YWxvciBkZSBsYSBjbGFzZSBhIHByZWRlY2lyDQoNCi0gYmluczogY2FudGlkYWQgZGUgZ3J1cG9zIGRlbCBncsOhZmljbw0KDQotIGNvbG9yOiBjb2xvciBkZSBsb3MgcHVudG9zDQoNCi0gbnVkZ2VfeDogZGVzcGxhemFtaWVudG8gZGUgbGEgZXRpcXVldGEgZW4gZWwgZWplIHgNCg0KLSBudWRnZV95OiBkZXNwbGF6YW1pZW50byBkZSBsYSBldGlxdWV0YSBlbiBlbCBlamUgeQ0KDQoNCmBgYHtyLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KDQogICMgRnVuY2nDs24gcGFyYSBnZW5lcmFyIGxvcyBncsOhZmljb3MNCkhvc21lcl9MZW1lc2hvd19wbG90IDwtIGZ1bmN0aW9uKGRhdGFzZXQsIHByZWRpY3RlZF9jb2x1bW4sIGNsYXNzX2NvbHVtbiwgYmlucywgcG9zaXRpdmVfdmFsdWUsIGNvbG9yPSdmb3Jlc3RncmVlbicsIG51ZGdlX3g9MCwgbnVkZ2VfeT0wLjA1KXsNCiAgDQogICMgQXNpZ25hciBsb3MgZ3J1cG9zIGEgbGFzIG9ic2VydmFjaW9uZXMgZGUgYWN1ZXJkbyBhIGxhIHByb2JhYmlsaWRhZCBwcmVkaWNoYQ0KICBkYXRhc2V0Wydncm91cCddIDwtIGJpbihkYXRhc2V0W3ByZWRpY3RlZF9jb2x1bW5dLCBuYmlucyA9IGJpbnMsIG1ldGhvZCA9ICdsJywgbGFiZWxzPWMoMTpiaW5zKSkNCiAgDQogICMgQ29udGFyIGxhIGNhbnRpZGFkIGRlIGNhc29zIHBvc2l0aXZvcyBwb3IgZ3J1cG8NCiAgcG9zaXRpdmVfY2xhc3MgPC0gZGF0YXNldCAlPiUgZmlsdGVyKCEhc3ltKGNsYXNzX2NvbHVtbik9PXBvc2l0aXZlX3ZhbHVlKSAlPiUgZ3JvdXBfYnkoZ3JvdXApICU+JSBjb3VudCgpDQogIA0KICAjIE9idGVuZXIgbGEgbWVkaWEgZGUgbGFzIHByZWRpY2Npb25lcyBwb3IgZ3J1cG8NCiAgSExfZGYgPC0gZGF0YXNldCAlPiUgZ3JvdXBfYnkoZ3JvdXApICU+JSBzdW1tYXJpc2UocHJlZD1tZWFuKCEhc3ltKHByZWRpY3RlZF9jb2x1bW4pKSwgY291bnQ9bigpKSAlPiUNCiAgICAgICAgICAgIGlubmVyX2pvaW4oLixwb3NpdGl2ZV9jbGFzcykgJT4lDQogICAgICAgICAgICBtdXRhdGUoZnJlcT1uL2NvdW50KQ0KICAjIEdyw6FmaWNvIA0KICBITV9wbG90IDwtIGdncGxvdChITF9kZiwgYWVzKHg9cHJlZCwgeT1mcmVxKSkgKyANCiAgICBnZW9tX3BvaW50KGFlcyhzaXplPW4pLCBjb2xvcj1jb2xvcikgKw0KICAgIGdlb21fdGV4dChhZXMobGFiZWw9biksbnVkZ2VfeSA9IG51ZGdlX3kpKw0KICAgIGdlb21fYWJsaW5lKHNsb3BlID0gMSwgaW50ZXJjZXB0ID0gMCwgbGluZXR5cGU9J2Rhc2hlZCcpICsgDQogICAgdGhlbWVfYncoKSArDQogICAgbGFicyh0aXRsZT0nSG9zbWVyLUxlbWVzaG93Jywgc2l6ZT0nQ2Fzb3MnLCB4PSJQcm9iYWJpbGlkYWQgUHJlZGljaGEiLCB5PSJGcmVjdWVuY2lhIG9ic2VydmFkYSIpDQogIHJldHVybihITV9wbG90KQ0KfQ0KDQpgYGANCg0KR2VuZXJhbW9zIGxvcyBncsOhZmljb3MgcGFzYW5kb2xlIGxvcyBwYXLDoW1ldHJvcy4gDQoNCmBgYHtyLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KIyBtb2RlbG8gYnVlbm8NCkhvc21lcl9MZW1lc2hvd19wbG90KHByZWRpY3Rpb25fZnVsbCwgJy5maXR0ZWQnLCAnYXNpc3RlbmNpYScsIDEwLCAxLCBjb2xvciA9ICJmb3Jlc3RncmVlbiIpICsNCiAgbGFicyhzdWJ0aXRsZT0iTW9kZWxvIGJ1ZW5vIikNCiMgbW9kZWxvIG1hbG8NCkhvc21lcl9MZW1lc2hvd19wbG90KHByZWRpY3Rpb25fYmFkLCAnLmZpdHRlZCcsICdhc2lzdGVuY2lhJywgMTAsIDEsIGNvbG9yID0gImZpcmVicmljayIpICsgbGFicyhzdWJ0aXRsZT0iTW9kZWxvIG1hbG8iKQ0KDQpgYGANCg0KRW4gbG9zIGdyw6FmaWNvcyBkZSBIb3NtZXItTGVtZXNob3cgb2JzZXJ2YW1vczoNCg0KLSBFbiBlbCBlamUgZGUgYWJzY2lzYXMgbGEgcHJvYmFiaWxpZGFkIHByZWRpY2hhIGRlIHN1cGVydml2ZW5jaWEuDQoNCi0gRW4gZWwgZWplIGRlIG9yZGVuYWRhcyBsYSBmcmVjdWVuY2lhIGRlIGNsYXNlLCBlbCBjb2NpZW50ZSBlbnRyZSBjYW50aWRhZCBkZSBpbmRpdmlkdW9zIFN1cnZpdmVkIHkgZWwgdG90YWwgZGUgaW5kaXZpZHVvcy4NCg0KLSBMYSBsw61uZWEgcHVudGVhZGEgZGVzaWduYSBsYSBpZ3VhbGRhZCBlbnRyZSBwcm9iYWJpbGlkYWQgcHJlZGljaGEgeSBmcmVjdWVuY2lhIGRlIGNsYXNlLg0KDQotIExvcyBjw61yY3Vsb3MsIHF1ZSBzZSBjb25zdHJ1eWVuIGRlIGxhIHNpZ3VpZW50ZSBtYW5lcmE6U2UgZGl2aWRlbiBhIGxhcyBvYnNlcnZhY2lvbmVzIGVuIGJpbnMgZW4gYmFzZSBhIGxhIHByb2JhYmlsaWRhZCBwcmVkaWNoYS4gU2UgY2FsY3VsYSBsYSBmcmVjdWVuY2lhIGRlIGNsYXNlIHBhcmEgY2FkYSBiaW4uIEVuIGJhc2UgYSBlc3RhcyBkb3MgY29vcmRlbmFkYXMgc2UgdWJpY2EgYWwgY8OtcmN1bG8gZW4gZWwgZ3LDoWZpY28uIEVsIG7Dum1lcm8geSB0YW1hw7FvIGluZGljYW4gbGEgY2FudGlkYWQgZGUgb2JzZXJ2YWNpb25lcyBlbiBkaWNobyBncnVwby4NCkFxdWVsbG9zIGPDrXJjdWxvcyBxdWUgc2UgdWJpcXVlbiBwb3IgZW5jaW1hIGRlIGxhIGzDrW5lYSBwdW50ZWFkYSBpbmRpY2FuIHF1ZSBlbCBtb2RlbG8gZXN0w6Egc3ViZXN0aW1hbmRvIGxhIHByb2JhYmlsaWRhZCBwYXJhIGRpY2hvcyBncnVwb3MuIE1pZW50cmFzIHF1ZSBzaSBsb3MgY8OtcmN1bG9zIHNlIHViaWNhbiBwb3IgZGViYWpvIGVsIG1vZGVsbyBlc3TDoSBzb2JyZWVzdGltYW5kbyBsYSBwcm9iYWJpbGlkYWQgcGFyYSBkaWNob3MgZ3J1cG9zLg0KDQpFbCBlbCBtb2RlbG8gICoqYXNpc3RlbmNpYX4gZnJhbmphX2V0YXJpYSArIHJlZ2lvbiArIHAxMDcqKiBvYnNlcnZhbW9zIHF1ZSBsYSBtYXlvcsOtYSBkZSBsb3MgY2lyY3Vsb3Mgc2UgdWJpY2FuIHNvYnJlIGxhIGzDrW5lYSBwdW50ZWFkYS4gRXMgZGVjaXIsIHNlIHByZXNlbnRhIG1heW9yIGlndWFsZGFkIGVudHJlIHByb2JhYmlsaWRhZCBwcmVkaWNoYSB5IGZyZWN1ZW5jaWEgZGUgKiphc2lzdGVuY2lhKiouIFNpbiBlbWJhcmdvIHBhcmVjaWVyYSBleGlzdGlyIGNpZXJ0YSBzb2JyZWVzdGltYWNpw7NuIGRlIGxhIHByb2JhYmlsaWRhZCAoYWxyZWRlZG9yIGRlIDAuNjgpIHBhcmEgODQgb2JzZXJ2YWNpb25lcyBkZSBwb2NvIG3DoXMgZGUgMC42MCBkZSBmcmVjdWVuY2lhIG9ic2VydmFkYS4gTG8gY29udHJhcmlvIHN1Y2VkZSBjb24gZWwgZ3J1cG8gZGUgMjkgb2JzZXJ2YWNpb25lcyB5IGRlIDEyOSBvYnNlcnZhY2lvbmVzIGRlIGxhcyBwcm9iYWJpbGlkYWRlcyBwcmVkaWNoYXMgZGUgdmFsb3IgMC4zNSB5IDAuOTUsIHJlc3BlY3RpdmFtZW50ZS4gDQoNCg0KMykgR3JhZmljYW1vcyBsYXMgKipDdXJ2YXMgUk9DKioNCg0KYGBge3IsbWVzc2FnZT1GQUxTRX0NCiMgQ2FsY3VsYW1vcyBjdXJ2YXMgUk9DDQpyb2NfZnVsbCA8LSByb2MocmVzcG9uc2U9cHJlZGljdGlvbl9mdWxsJGFzaXN0ZW5jaWEsIHByZWRpY3Rvcj1wcmVkaWN0aW9uX2Z1bGwkLmZpdHRlZCkNCnJvY19iYWQgPC0gcm9jKHJlc3BvbnNlPXByZWRpY3Rpb25fYmFkJGFzaXN0ZW5jaWEsIHByZWRpY3Rvcj1wcmVkaWN0aW9uX2JhZCQuZml0dGVkKQ0KYGBgDQoNCkdyYWZpY2Ftb3MgYW1iYXMgZW4gdW4gbWlzbW8gcGxvdC4NCg0KYGBge3J9DQpnZ3JvYyhsaXN0KGZ1bGw9cm9jX2Z1bGwsIGJhZD1yb2NfYmFkKSwgc2l6ZT0xKSArIA0KICBnZW9tX2FibGluZShzbG9wZSA9IDEsIGludGVyY2VwdCA9IDEsIGxpbmV0eXBlPSdkYXNoZWQnKSArDQogIHRoZW1lX2J3KCkgKyANCiAgbGFicyh0aXRsZT0nQ3VydmFzIFJPQycsIGNvbG9yPSdNb2RlbG8nKQ0KcHJpbnQocGFzdGUoJ0FVQzogTW9kZWxvIGJ1ZW5vJywgcm91bmQocm9jX2Z1bGwkYXVjLDMpKSkNCnByaW50KHBhc3RlKCdBVUM6IE1vZGVsbyBtYWxvJywgcm91bmQocm9jX2JhZCRhdWMsMykpKQ0KDQpgYGANCg0KVmVtb3MgcXVlIGxhIGN1cnZhIFJPQyBkZWwgIm1vZGVsbyBtYWxvIiBjYXNpIHNlIHBlZ2EgYSBsYSBsw61uZWEgcHVudGVhZGEsIGluZGljYW5kbyBxdWUgbG9zIHZhbG9yZXMgZGUgcHJlZGljY2lvbmVzIGRlIGVzdGUgbW9kZWxvIHNlIGFzZW1lamFuIGEgInRpcmFyIHVuYSBtb25lZGEiLCBlcyBkZWNpciwgZXhpc3RlIGNhc2kgbGEgbWlzbWEgcHJvYmFiaWxpZGFkICgwLjUwKSBkZSBwcmVkZWNpciBxdWUgbGEgcGVyc29uYSBoYXlhIGlkbyBhbCBjaW5lLCBhbCB0ZWF0cm8geS9vIGEgcmVjaXRhbGVzIG8gbm8gZW4gZWwgw7psdGltbyBhw7FvLCBjdWFuZG8gc2UgaGFjZSBsYSBlc3RpbWFjacOzbiBlbiBmdW5jacOzbiBkZSBsYSB2YXJpYWJsZSAqKnNleG8qKiBzb2xhbWVudGUuIFBvciBvdHJhIHBhcnRlLCB2ZW1vcyBxdWUgbnVlc3RybyBtZWpvciBtb2RlbG8gc2UgYWxlamEgYmFzdGFudGUgZGUgZXN0YSBzaXR1YWNpw7NuIChBVUMgMC43MjQpLg0KDQoNCiMjIyBQdW50byBkZSBjb3J0ZQ0KDQpIYXN0YSBhaG9yYSBoZW1vcyBldmFsdWFkbyBudWVzdHJvIG1lam9yIG1vZGVsbyBkZSBtYW5lcmEgZ2VuZXJhbCwgcGVybyBlbCByZXN1bHRhZG8gZmluYWwgZGViZSBjb25zaXN0aXIgZW4gYXNpZ25hciBhIGxhIHBlcnNvbmEgdW5hIGNsYXNlIHByZWRpY2hhLiBFbiBudWVzdHJvIGNhc28sIGRlYmVtb3MgZXN0YWJsZWNlciB1biBwdW50byBkZSBjb3J0ZSBzZWfDum4gZWwgY3VhbCBwb2RhbW9zIHNlcGFyYXIgYSBsYXMgcGVyc29uYXMgcXVlIGZ1ZXJvbiBlbCDDumx0aW1vIGHDsW8gYWwgdGVhdHJvLCBhbCBjaW5lIHkvbyBhIHJlY2l0YWxlcyBkZSBsYXMgcXVlIG5vLiANCg0KQSBjb250aW51YWNpw7NuLCBwcm9iYW1vcyB2YXJpb3MgcHVudG9zIGRlIGNvcnRlIHkgZ3JhZmljYW1vcyBlbCAqYWNjdXJhY3ksIGxhIHNlbnNpYmlsaWRhZCBvIHJlY2FsbCwgbGEgZXNwZWNpZmljaWRhZCB5IGxhIHByZWNpc2nDs24qIHBhcmEgY2FkYSB1bm8gZGUgZWxsb3MuDQoNCnwgQ2xhc2VzIHByZWRpY2hhcyAvIENsYXNlcyB8IE5lZ2F0aXZhIHwgUG9zaXRpdmEgfA0KfC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tfC0tLS0tLS0tLXwtLS0tLS0tLS0tfA0KfCBOZWdhdGl2YSAgICAgICAgICAgICAgICAgfCBUcnVlIE5lZyB8IEZhbHNlIE5lZyB8DQp8IFBvc2l0aXZhICAgICAgICAgICAgICAgICB8IEZhbHNlIFBvcyB8IFRydWUgUG9zIHwNCg0KUmVjb3JkZW1vcyBxdWU6DQoNCiRhY2N1cmFjeSA9IFxmcmFje1RQK1ROfXtUUCtGUCtGTitUTn0kDQoNCiRzZW5zaXRpdml0eSA9IHJlY2FsbCA9IFxmcmFje1RQfXtUUCtGTn0kDQoNCiRzcGVjaWZpY2l0eSA9IFxmcmFje1ROfXtUTitGUH0kDQoNCiRwcmVjaXNpb24gPSBcZnJhY3tUUH17VFArRlB9JA0KDQpgYGB7cn0NCnByZWRpY3Rpb25fbWV0cmljcyA8LSBmdW5jdGlvbihjdXRvZmYsIHByZWRpY3Rpb25zPXByZWRpY3Rpb25fZnVsbCl7DQogIHRhYiA8LSBwcmVkaWN0aW9ucyAlPiUgDQogICAgbXV0YXRlKHByZWRpY3RlZF9jbGFzcyA9IGlmX2Vsc2UoLmZpdHRlZCA+IGN1dG9mZiwgMSwgMCksDQogICAgICAgICAgIGFzaXN0ZW5jaWEgPSBmYWN0b3IoYXNpc3RlbmNpYSkpDQogIHUgPC0gdW5pb24odGFiJHByZWRpY3RlZF9jbGFzcywgdGFiJGFzaXN0ZW5jaWEpDQogIHQgPC0gdGFibGUoZmFjdG9yKHRhYiRwcmVkaWN0ZWRfY2xhc3MsIHUpLCBmYWN0b3IodGFiJGFzaXN0ZW5jaWEsIHUpKQ0KICBjb25mdXNpb25NYXRyaXgodCwgcG9zaXRpdmUgPSAiMSIpICU+JQ0KICAgIHRpZHkoKSAlPiUNCiAgICBzZWxlY3QodGVybSwgZXN0aW1hdGUpICU+JQ0KICAgIGZpbHRlcih0ZXJtICVpbiUgYygnYWNjdXJhY3knLCAnc2Vuc2l0aXZpdHknLCAnc3BlY2lmaWNpdHknLCAncHJlY2lzaW9uJykpICU+JQ0KICAgIG11dGF0ZShjdXRvZmYgPSBjdXRvZmYpDQp9DQpjdXRvZmZzID0gc2VxKDAuMDUsMC45NSwwLjAxKQ0KbG9naXRfcHJlZCA9IG1hcF9kZihjdXRvZmZzLCBwcmVkaWN0aW9uX21ldHJpY3MpICU+JSANCiAgbXV0YXRlKHRlcm0gPSBhcy5mYWN0b3IodGVybSksIGVzdGltYXRlID0gcm91bmQoZXN0aW1hdGUsIDMpKQ0KZ2dwbG90KGxvZ2l0X3ByZWQsIGFlcyhjdXRvZmYsZXN0aW1hdGUsIGdyb3VwPXRlcm0sIGNvbG9yPXRlcm0pKSArIGdlb21fbGluZShzaXplPTEpICsNCiAgdGhlbWVfYncoKSArDQogIGxhYnModGl0bGU9ICdBY2N1cmFjeSwgU2Vuc2l0aXZpdHksIFNwZWNpZmljaXR5IHkgUHJlY2lzaW9uJywgc3VidGl0bGU9ICdNb2RlbG8gYnVlbm8nLCBjb2xvcj0iIikNCmBgYA0KRW4gZWwgZ3LDoWZpY28gb2JzZXJ2YW1vcyBxdWUgaGF5IHVuYSB0ZW5kZW5jaWEgY3JlY2llbnRlIGVuIGN1YW50byBhIGxhIGVzdGltYWNpw7NuIGRlIG51ZXN0cm8gbWVqb3IgbW9kZWxvIHBhcmEgcHVudG9zIGRlIGNvcnRlIG1heW9yZXMgZW4gY3VhbnRvIGEgKnNwZWNpZmljaXR5KiwgbWllbnRyYXMgcXVlIGV4aXN0ZSB1bmEgdGVuZGVuY2lhIGRlY3JlY2llbnRlIHBhcmEgcHVudG9zIGRlIGNvcnRlIG1heW9yZXMgYWwgZXN0aW1hciBsYSAqc2Vuc2liaWxpdHkqLiBFbiBnZW5lcmFsLCBlbCAqYWNjdXJhY3kqIHkgbGEgKnByZWNpc2lvbiogdG9tYW4gdmFsb3JlcyBwb3IgZW5jaW1hIGRlbCAwLjUgcGFyYSBjdWFscXVpZXIgcHVudG8gZGUgY29ydGUsIHBlcm8gZWwgKmFjY3VyYWN5KiBubyB0b21hIG51bmNhIHZhbG9yZXMgbWF5b3JlcyBhbCAwLjcsIG1pZW50cmFzIHF1ZSBsYSAqcHJlY2lzaW9uKiB0aWVuZSB0ZW5kZW5jaWEgY3JlY2llbnRlIGEgbWF5b3IgdmFsb3IgZGUgcHVudG8gZGUgY29ydGUuDQoNCkVuIG51ZXN0cm8gY2FzbywgcG9yIGxhIG5hdHVyYWxlemEgZGVsIHByb2JsZW1hIGFib3JkYWRvLCBlbGVnaW1vcyB1biBwdW50byBkZSBjb3J0ZSBwYXJhIHBvbmRlcmFyIGxhIHNlbnNpYmlsaXR5LCBvICp0YXNhIGRlIHZlcmRhZGVyb3MgcG9zaXRpdm9zKiwgZW4gMC40OyB5YSBxdWUgZW4gZXNlIHB1bnRvIHRlbmVtb3MgdW4gdmFsb3IgcmVsYXRpdmFtZW50ZSBhbHRvIHBhcmEgZXN0YSB0YXNhLCByZXNpZ25hbmRvIGxvcyB2YWxvcmVzIGRlICpzcGVjaWZpY2l0eSogcGVybyBjb24gZXN0aW1hY2lvbmVzIHBvciBlbmNpbWEgZGVsIDAuNSB0YW50byBkZSAqYWNjdXJhY3kqIGNvbW8gKnByZWNpc2lvbiouDQoNCg0KIyMjIERhdGFzZXQgZGUgdGVzdGluZw0KDQpTZWxlY2Npb25hbW9zIGVsIG1vZGVsbyAqKmFzaXN0ZW5jaWF+IGZyYW5qYV9ldGFyaWEgKyByZWdpb24gKyBwMTA3KiosIHlhIHF1ZSBlcyBlbCBxdWUgbWF4aW1pemFiYSBlbCBwb3JjZW50YWplIGRlIGRldmlhbmNlIGV4cGxpY2FkYSB5LCBlbiBiYXNlIGEgbG8gcXVlIHNlIG9ic2VydmEgZW4gZWwgZ3LDoWZpY28gYW50ZXJpb3IsIGRlZmluaW1vcyB1biBwdW50byBkZSBjb3J0ZSBlbiAwLjQuDQoNCkNhbGN1bGFtb3MgbGEgbWF0cml6IGRlIGNvbmZ1c2nDs24gcGFyYSBsb3MgZGF0YXNldHMgZGUgdHJhaW4geSB0ZXN0Lg0KDQpgYGB7cixtZXNzYWdlPUZBTFNFfQ0Kc2VsX2N1dG9mZiA9IDAuNCANCiMgQ3JlYW1vcyBlbCBtb2RlbG8NCmZ1bGxfbW9kZWwgPC0gZ2xtKGxvZ2l0X2Zvcm11bGFzJGZyYW5qYV9yZWdpb25fUFNFLCBmYW1pbHkgPSAnYmlub21pYWwnLCBkYXRhID0gdHJhaW5fZGF0YSkNCiMgY2FsY3VsYW1vcyBsYXMgcHJlZGljY2lvbmVzIHNvYnJlIGVsIGRhdGFzZXQgZGUgdHJhaW4NCnRhYmxlX3RyYWluID0gYXVnbWVudCh4ID0gZnVsbF9tb2RlbCwgdHlwZS5wcmVkaWN0PSdyZXNwb25zZScpDQojIENsYXNpZmljYW1vcyB1dGlsaXphbW9zIGVsIHB1bnRvIGRlIGNvcnRlDQp0YWJsZV90cmFpbiA9IHRhYmxlX3RyYWluICU+JSANCiAgbXV0YXRlKHByZWRpY3RlZF9jbGFzcyA9IGlmX2Vsc2UoLmZpdHRlZD5zZWxfY3V0b2ZmLCAxLCAwKSAlPiUgYXMuZmFjdG9yKCksIA0KICAgICAgICAgYXNpc3RlbmNpYSA9IGZhY3Rvcihhc2lzdGVuY2lhKSkNCiMgQ3JlYW1vcyBsYSBtYXRyaXogZGUgY29uZnVzacOzbg0KY29uZnVzaW9uTWF0cml4KHRhYmxlKHRhYmxlX3RyYWluJHByZWRpY3RlZF9jbGFzcywgdGFibGVfdHJhaW4kYXNpc3RlbmNpYSksIHBvc2l0aXZlID0gIjEiKQ0KYGBgDQoNCg0KYGBge3IsbWVzc2FnZT1GQUxTRX0NCiMgQWdyZWdhbW9zIGxhIHByZWRpY2Npb25lcyBhbCBkYXRhc2V0IGRlIHRlc3Rlbw0KdGFibGVfdGVzdCA9IGF1Z21lbnQoeCA9IGZ1bGxfbW9kZWwsIG5ld2RhdGE9dGVzdF9kYXRhLCB0eXBlLnByZWRpY3Q9J3Jlc3BvbnNlJykgDQojIENsYXNpZmljYW1vcyB1dGlsaXphbW9zIGVsIHB1bnRvIGRlIGNvcnRlDQp0YWJsZV90ZXN0ID0gdGFibGVfdGVzdCAlPiUgDQogIG11dGF0ZShwcmVkaWN0ZWRfY2xhc3MgPSBpZl9lbHNlKC5maXR0ZWQ+c2VsX2N1dG9mZiwgMSwgMCkgJT4lIGFzLmZhY3RvcigpLCANCiAgICAgICAgIGFzaXN0ZW5jaWEgPSBmYWN0b3IoYXNpc3RlbmNpYSkpDQojIENyZWFtb3MgbGEgbWF0cml6IGRlIGNvbmZ1c2nDs24NCmNvbmZ1c2lvbk1hdHJpeCh0YWJsZSh0YWJsZV90ZXN0JHByZWRpY3RlZF9jbGFzcywgdGFibGVfdGVzdCRhc2lzdGVuY2lhKSwgcG9zaXRpdmUgPSAiMSIpDQpgYGANCkNvbiBlc2UgcHVudG8gZGUgY29ydGUgcG9kZW1vcyB2ZXIgcXVlIGRhZGEgbGEgY2xhc2UgMSAoYXNpc3Rpw7MpIGxvcyB2YWxvcmVzIHBhcmEgbGFzICptw6l0cmljYXMgZW4gZWwgZGF0YXNldCBkZSB0ZXN0ZW8qIHNvbjoNCg0KLSBBY2N1cmFjeTogMC42MTcxICAgICAgICAgICAgICAgICAgDQotIFByZWNpc2lvbjogMC41ODgxDQotIFNlbnNpdGliaXR5OiAwLjg3MzMNCi0gU3BlY2lmaWNpdHk6IDAuMzQxMiANCg0KQWwgY29tcGFyYXJsYXMgY29uIGxhcyBtw6l0cmljYXMgZGVsIGRhdGEgc2V0IGRlIGVudHJlbmFtaWVudG8sIHNlIG9ic2VydmEgcXVlIGRpc21pYnV5ZW4gbGV2ZW1lbnRlLiBFc3RvIGVzIGFzw60gcG9ycXVlIGVsIG1vZGVsbyBmdWUgZW50cmVuYWRvIGVuIGVsIG90cm8gZGF0YXNldCAodHJhaW4pIHkgZXNvcyBtaXNtb3MgZGF0b3Mgc2UgdXRpbGl6YXJvbiBwYXJhIGVsZWdpciBlbCBwdW50byBkZSBjb3J0ZS4gU2luIGVtYmFyZ28sIGVzdGEgZGlzbWludWNpw7NuIG5vIGVzIGxsYW1hdGl2YS4gRWwgQWNjdXJhY3kgcGFzYSBkZSAwLjY0NDggYSAwLjYxNzEsIGxhIFByZWNpc2lvbiBkZSAwLjYxMTEgYSAwLjU4ODEsIGxhIFNlbnNpdGliaXR5IGRlIDAuODk3MCBhIDAuODczMyB5IGxhIFNwZWNpZmljaXR5LCBkZSAwLjM2MzUgYSAwLjM0MTIuDQoNCkxvcyB2YWxvcmVzIHN1cGVyaW9yZXMgYSAwLjg1IGRlIGxhIG3DqXRyaWNhICpTZW5zaXRpYml0eSogKHVuYSBtZWRpZGEgZGUgbGEgcHJvcG9yY2nDs24gZGUgY2Fzb3MgcG9zaXRpdm9zIHJlYWxlcyBxdWUgc2UgcHJvbm9zdGljYXJvbiBjb21vIHBvc2l0aXZvcywgZXMgZGVjaXIsIFRydWUgUG9zaXRpdmUpLCBpbmRpY2EgcXVlIGVsIG1vZGVsbyBmdW5jaW9uYXLDrWEgcmVsYXRpdmFtZW50ZSBiaWVuIHBhcmEgaWRlbnRpZmljYXIgYSBsYXMgcGVyc29uYXMgcXVlIGFzaXN0aWVyb24gZW4gZWwgw7psdGltbyBhw7FvIGFsIGNpbmUsIGFsIHRlYXRybyB5L28gYSByZWNpdGFsZXMuICANCg0KRmluYWxtZW50ZSwgdGFudG8gZW4gZWwgZGF0YXNldCBkZSBlbnRyZW5hbWllbnRvIGNvbW8gZWwgZGUgdGVzdGVvLCBzZSBvYmVydmEgcXVlIGVsICphY2N1cmFjeSBlcyBlc3RhZMOtc3RpY2FtZW50ZSBkaXN0aW50byBhIGxhIGNsYXNlIG1heW9yaXRhcmlhKiAocC12YWx1ZSBbQWNjID4gTklSXSA8IDAuMDUpLiANCg0KDQojIyBDb25jbHVzaW9uZXMNCg0KUmV0b21hbW9zIGxhICoqaGlww7N0ZXNpcyoqIGRlIGxhIHF1ZSBwYXJ0aW1vczogbGFzIG11amVyZXMgY29uIGhpam9zIHkvbyBxdWUgY29udml2ZW4gY29uIHBlcnNvbmFzIG1heW9yZXMgZGUgNjUgYcOxb3MgdmFuIGFsIGNpbmUsIGFsIHRlYXRybyB5L28gYSByZWNpdGFsZXMgZW4gbWVub3IgbWVkaWRhIHF1ZSBsb3MgdmFyb25lcyBlbiBsYSBtaXNtYSBjb25kaWNpw7NuLiANCg0KTHVlZ28gZGUgbGEgYXBsaWNhY2nDs24gZGUgbG9zIG1vZGVsb3MgZGUgcmVncmVzacOzbiBsb2fDrXN0aWNhIHByZXNlbnRhZG9zLCBwb2Ryw61hbW9zIGFmaXJtYXIgcXVlICpsYXMgdmFyaWFibGVzIHF1ZSB0aWVuZSBtYXlvciBpbmZsdWVuY2lhIHNvYnJlIGxhIGFzaXRlbmNpYSBvIG5vIGEgZXN0ZSB0aXBvIGRlIGFjdGl2aWRhZGVzIGN1bHR1cmFsZXMgc29uKjogDQoNCi0gbGEgKnJlZ2nDs24gZGVsIHBhw61zKjogc2llbmRvIGxhcyBwZXJzb25hcyBxdWUgdml2ZW4gZW4gbGEgQ2l1ZGFkIEF1dMOzbm9tYSBkZSBCdWVub3MgQWlyZXMgbGFzIHF1ZSB0aWVuZSBtYXlvciBwcm9iYWJpbGlkYWQgZGUgaGFiZXIgYXNpc3RpZG8gZW4gZWwgw7psdGltbyBhw7FvIGFsIGNpbmUsIGFsIHRlYXRybyB5L28gYSBlc3BlY3TDoWN1bG9zIGRlIG3DunNpY2EgZW4gdml2by4NCi0gbGEgKmVkYWQqOiBxdWllbmVzIHRpZW5lbiBtYXlvciBwcm9iYWJpbGlkYWQgZGUgaGFiZXIgYXNpc3RpZG8gYWwgY2luZSwgYWwgdGVhdHJvIHkvbyBhIHJlY2l0YWxlcyBlbiBlbCDDumx0aW1vIGHDsW8gc29uIGxvcyBtw6FzIGrDs3ZlbmVzIChlbnRyZSAxMiB5IDE3IGHDsW9zKS4gQWwgbWlyYXIgbG9zIGNvZWZpY2llbnRlcywgc2Ugb2JzZXJ2YSBxdWUgbGEgcHJvYmFibGlkYWQgZGUgaGFiZXIgYXNpc3RpZG8gYSBlc3RlIHRpcG8gZGUgZXZlbnRvcyBlbiBlbCDDumx0aW1vIGHDsW8sIGRpc21pbnV5ZSBhIG1lZGlkYSBxdWUgbGEgZWRhZCBkZSBsYSBwZXJzb25hIGF1bWVudGEuIA0KLSAqUHJpbmNpcGFsIHNvc3TDqW4gZWNvbsOzbWljbyBkZWwgaG9nYXIgKFBTRSkqOiBsYXMgcGVyc29uYXMgcXVlIG5vIHNvbiBlbCBwcmluY2lwYWwgc29zdMOpbiBlY29uw7NtaWNvIGRlIGxvcyBob2dhcmVzIGVuIGxvcyBxdWUgdml2ZW4gdGllbmVuIG1heW9yIHByb2JhYmlsaWRhZCBkZSBoYWJlciBhc2lzdGlkbyBhbCBjaW5lLCBhbCB0ZWF0cm8geS9vIGEgcmVjaXRhbGVzIGR1cmFudGUgZWwgw7psdGltbyBhw7FvIHF1ZSBsYXMgcXVlIHPDrSBsbyBzb24uIEVzdG8gcHVlZGUgZXN0YXIgcmVsYWNpb25hZG8gY29uIGxvIG9iZXJ2YWRvIGVuIGVsIHB1bnRvIGFudGVyaW9yOiBzaSBxdWllbmVzIHRpZW5lbiBtYXlvciBwcm9iYWJpbGlkYWQgZGUgaGFiZXIgYXNpc3RpZG8gYSBlc3RlIHRpcG8gZGUgYWN0aXZpZGFkZXMgZHVyYW50ZSBlbCDDumx0aW1vIGHDsW8gc29uIGrDs3ZlbmVzIGVudHJlIDEyIHkgMTcsIHBvZHLDrWEgc2VyIGVzcGVyYWJsZSBxdWUgcXVpZW5lcyB0ZW5nYW4gbWF5b3JlcyBwcm9iYWJpbGlkYWRlcyBkZSBhc2lzdGVuY2lhIG5vIHNlYW4gZWwgcHJpbmNpcGFsIHNvc3TDqW4gZWNvbsOzbWljbyBkZSBsb3MgaG9nYXJlcyBlbiBsb3MgcXVlIHZpdmVuLg0KDQpTaSBiaWVuIGxhIHZhcmlhYmxlICpjb252aXZlX25pw7F4c19hZHVsdG9zbWF5b3Jlcyogbm8gZm9ybcOzIHBhcnRlIGRlbCBtb2RlbG8gKG3Dumx0aXBsZSkgcXVlIGxvZ3LDsyBtaW5pbWl6YXIgbGEgZGV2aWFuY2UsIGFsIHNlciBhbmFsaXphZGEgZGUgZm9ybWEgaW5kaXZpZHVhbCwgc8OtIHByZXNlbnTDsyB1bmEgZGlmZXJlbmNpYSBlc3RhZMOtc3RpY2FtZW50ZSBzaWduaWZpY2F0aXZhOiBsYXMgcGVyc29uYXMgcXVlIGNvbnZpdmVuIGNvbiBuacOxeHMsIGFkb2xlY2VudGVzIHkvbyBhZHVsdG9zIG1heW9yZXMgdGllbmVuIHVuYSBwcm9iYWJpbGlkYWQgZGUgYXNpc3RlbmNpYSBhbCBjaW5lLCB0ZWF0cm8geS9vIHJlY2l0YWxlcyBlbiBlbCDDumx0aW1vIGHDsW8gbWVub3IgcXVlIGxhcyBwZXJzb25hcyBxdWUgbm8gY29udml2ZW4uIEVuIGzDrW5lYSBjb24gbnVlc3RyYSBoaXDDs3Rlc2lzIGluY2lhbCwgcG9kcsOtYSBwZW5zYXJzZSBlbnRvbmNlcyBxdWUgZWwgaGVjaG8gZGUgY29udml2aXIgY29uIG1lbm9yZXMgeS9vIHBlcnNvbmFzIG1heW9yZXMgZGUgNjUgYcOxb3MgcmVkdWNlIGxhIHByb2JhYmlsaWRhZCBkZSBhc2lzdGVuY2lhIGEgZXN0ZSB0aXBvIGRlIGFjdGl2aWRhZGVzLiBRdWVkYSBwZW5kaWVudGUgcGFyYSBmdXR1cm9zIHRyYWJham9zIGFuYWxpemFyIHNpIGRpY2hhIHRlbmRlbmNpYSBwdWVkZSBhc29jaWFyc2UgbyBubyBjb24gdGVuZXIgYSBjYXJnbyB0YXJlYXMgZGUgY3VpZGFkby4gDQoNCkZpbmFsbWVudGUsIGxhIHZhcmlhYmxlICpzZXhvKiBubyBwcmVzZW50w7MgZGlmZXJlbmNpYXMgc2lnbmlmaWNhdGl2YXMgZW4gbmluZ3VubyBkZSBsb3MgbW9kZWxvcyBhcGxpY2Fkb3MuIA0KDQpDb25zaWRlcmFtb3MgcXVlIHBhcmEgKipmdXR1cmFzIGzDrW5lYXMgZGUgdHJhYmFqbyoqIHNlcsOtYSBpbnRlcmVzYW50ZSBwbGFudGVhciBsYSBleGNsdXNpw7NuIGRlIENBQkEgY29tbyByZWdpw7NuIGFuYWxpemFkYSB5L28gcmVwbGljYXIgZWwgYW7DoWxpc2lzIHBlcm8gc8OzbG8gc29icmUgbGFzIHBlcnNvbmFzIHF1ZSB0aWVuZW4gZW50cmUgMzAgeSA0OSBhw7FvcywgcGFyYSBvYnNlcnZhciBxdcOpIG9jdXJyZSBjb24gbGEgaW5mbHVlbmNpYSBkZWwgcmVzdG8gZGUgbGFzIHZhcmlhYmxlcyBlbiBsb3MgbW9kZWxvcy4gVGFtYmnDqW4gcHVlZGUgc2VyIHJlY29tZW5kYWJsZSBwYXJhIGZ1dHVyYXMgaW52ZXN0aWdhY2lvbmVzIGluY29ycG9yYXIgdmFyaWFibGVzIHJlbGFjaW9uYWRhcyBhbCBuaXZlbCBzb2Npb2Vjb27Ds21pY28gKE5TRSkgZGUgbGFzIHBlcnNvbmFzIGVudHJldmlzdGFkYXMuDQoNCg==